概念
用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象(通过复制生成对象)
角色
Client:客户端角色
负责使用复制实例的方法生成新的实例
Prototype:抽象原型角色
负责定义用于复制现有实例来生成新实例的方法
ConcreteProtetype:具体原型类
负责实现复制现有实例并生成新实例的方法
适用场景
对象种类繁多,午饭将他们整合到一个类的情景
难以根据类生成实例的情景
想解耦框架与生成的实例时(拷贝多个对象供使用,保护性拷贝)
ULM图
Product接口(Prototype)
product接口是复制功能接口,继承java.lang.Cloneable
public interface Product extends Cloneable{
//use方法是用于“使用”的方法,具体怎么“使用”,则被交给子类去实现。
public abstract void use(String s);
//creatClone方法是用于复制实例的方法
public abstract Product creatClone();
}
Manager类(Client)
Manager类使用Product接口来复制实例
public class Manager {
//保存实例的“名字”和“实例”之间的对应关系
private HashMap<String, Product> showcase=new HashMap<String, Product>();
//register方法将接收到的一组“名字”和“Product接口”注册到showcase中。
public void register(String name ,Product product){
showcase.put(name, product);
}
public Product create(String productname){
Product p=showcase.get(productname);
return p.creatClone();
}
}
MessageBox类(ConcreteProtorype)
装饰方框样式的具体原型,实现了Product接口,实现复制现有实例并生成新实例的方法。
public class MessageBox implements Product {
//保存的是装饰方框使用的字符样式
private char decochar;
public MessageBox(char decochar) {
this.decochar = decochar;
}
@Override
public void use(String s) {
int length=s.getBytes().length;
for (int i = 0; i < length+4; i++) {
System.out.print(decochar);
}
System.out.println("");
System.out.println(decochar+" "+s+" "+decochar);
for (int i = 0; i < length+4; i++) {
System.out.print(decochar);
}
System.out.println("");
}
//该方法用于复制自己
@Override
public Product creatClone() {
Product p=null;
try {
p=(Product) clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return p;
}
}
UnderlinePen类(ConcreteProtorype)
UnderlinePen类的实现几乎和MessageBox类一样,不同的可能只是use方法的实现。
public class UnderlinePen implements Product {
private char ulchar;
public UnderlinePen(char ulchar) {
this.ulchar = ulchar;
}
@Override
public void use(String s) {
int length = s.getBytes().length;
System.out.println("\""+s+"\"");
for (int i = 0; i <length+2; i++) {
System.out.print(ulchar);
}
System.out.println("");
}
@Override
public Product creatClone() {
Product p=null;
try {
p=(Product) clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return p;
}
}
Main类
生成Manager实例
public class Main {
public static void main(String[] args) {
Manager manager = new Manager();
UnderlinePen underlinePen=new UnderlinePen('~');
MessageBox mbox=new MessageBox('*');
MessageBox sbox=new MessageBox('/');
manager.register("Strong message", underlinePen);
manager.register("Waring Box", mbox);
manager.register("Slash Box", sbox);
Product p1=manager.create("Strong message");
p1.use("hello world");
Product p2=manager.create("Waring Box");
p2.use("hello world");
Product p3=manager.create("Slash Box");
p3.use("hello world");
}
}
设计与实现
设计思想:名片
1、名片类
package com.xjion.prototype;
public class Card implements Cloneable {
private String name;
private String company;
public void setName(String name) {
this.name = name;
}
public void setCompany(String company) {
this.company = company;
}
public Card() {
System.out.println("构造函数Card!!!");
}
//实现拷贝
@Override
protected Card clone() {
Card card = null;
try {
card = (Card) super.clone();
} catch (CloneNotSupportedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return card;
}
//查看的方法
public void show() {
System.out.println("name:"+name);
System.out.println("company:"+company);
}
}
2、使用
package com.xjion.prototype;
public class Client {
public static void main(String[] args) {
Card card = new Card();
card.setName("xjion");
card.setCompany("1688");
Card card2 = card.clone();
card2.setName("xjion1");
card2.setCompany("1688one");
card.show();
card2.show();
}
}
注意
实现Cloneable接口,只要重写clone方法就可以实现拷贝,不重写则会报错
clone方法不是Cloneable接口的,而是Object类的方法。
浅拷贝和深拷贝
浅拷贝
如果字段类型是引用类型的情景
1、创建一个公司类
package com.xjion.prototype;
public class Company {
private String name;
private String address;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public Company(String name, String address) {
super();
this.name = name;
this.address = address;
}
public Company() {
super();
// TODO Auto-generated constructor stub
}
}
2、名片
package com.xjion.prototype;
public class DeepCard implements Cloneable {
private String name;
private Company company = new Company();
public void setName(String name) {
this.name = name;
}
public void setCompany(String name,String address) {
this.company.setName(name);
this.company.setAddress(address);
}
@Override
protected DeepCard clone() {
DeepCard card = null;
try {
card = (DeepCard) super.clone();
} catch (CloneNotSupportedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return card;
}
public void show() {
System.out.println("cardName"+name+"---companyName:"+company.getName()+"---addr:"+company.getAddress());
}
}
3、使用
package com.xjion.prototype;
public class DeepClient {
public static void main(String[] args) {
DeepCard card = new DeepCard();
card.setName("小马");
card.setCompany("阿里", "北京");
DeepCard card2 = card.clone();
card2.setName("小马");
card2.setCompany("Tencent", "深圳");
card.show();
card2.show();
}
}
注意:
Object类提供的clone方法,不会拷贝对象内部数组和引用对象,所以导致他们仍然指向原地址,这种拷贝叫做浅拷贝。
company是引用类型,card被拷贝后,company仍指向原来的card对象的company的地址,所以每次设置都会覆盖前者。
深拷贝
拷贝Card对象的同时,也把company对象进行拷贝,使得对象永远指向自身的company地址。
1、公司
需要实现Cloneable接口重写clone方法
package com.xjion.prototype2;
public class Company implements Cloneable {
private String name;
private String address;
@Override
public Company clone() {
Company company = null;
try {
company = (Company) super.clone();
} catch (CloneNotSupportedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return company;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
2、名片
package com.xjion.prototype2;
public class Card implements Cloneable {
private String name;
private Company company = new Company();
@Override
protected Card clone() {
Card card = null;
try {
card = (Card) super.clone();
card.company = this.company.clone();
} catch (CloneNotSupportedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return card;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Company getCompany() {
return company;
}
public void setCompany(String name,String address) {
this.company.setName(name);
this.company.setAddress(address);
}
public void show() {
System.out.println("cardName"+name+"---companyName:"+company.getName()+"---addr:"+company.getAddress());
}
}
3、使用
package com.xjion.prototype2;
public class Client {
public static void main(String[] args) {
Card card = new Card();
card.setName("大马");
card.setCompany("Tencent","深圳");
Card card2 = card.clone();
card2.setName("小马");
card2.setCompany("1688", "北京");
card.show();
card2.show();
}
}
原型模式的特点
是在内存中二进制流的拷贝,比new对象的性能好,特别是需要大量对象时
直接在内存中拷贝,构造函数不会执行,减少了约束