gof23——原型模式
一、原型模式的应用场景
原型模式(Prototype)是指原型实例指定创建对象的种类,并且通过复制这些原型创建新的对象。
原型模式主要适用于以下场景:
- 类初始化消耗的资源较多
- 使用new生成一个对象需要非常繁琐的过程(数据准备、访问权限等)
- 构造函数比较复杂
- 在循环体中产生大量对象
二、浅克隆
一个标准的原型模式代码应该是这样设计的,先创建原型Prototype接口
public interface Prototype {
Prototype clone();
}
创建具体需要克隆的类ConcretePrototypeA
public class ConcretePrototypeA implements Prototype {
private Integer id;
private String name;
private List hobby;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List getHobby() {
return hobby;
}
public void setHobby(List hobby) {
this.hobby = hobby;
}
@Override
public String toString() {
return "ConcretePrototypeA{" +
"id=" + id +
", name='" + name + '\'' +
", hobby=" + hobby +
'}';
}
@Override
public Prototype clone() {
ConcretePrototypeA concretePrototypeA=new ConcretePrototypeA();
concretePrototypeA.setId(this.id);
concretePrototypeA.setName(this.name);
concretePrototypeA.setHobby(this.hobby);
return concretePrototypeA;
}
}
这里也可以不实现Prototype,直接实现Cloneable接口,重写clone方法
package prototype.shallow;
import java.io.*;
import java.util.List;
/**
* @author xxbb
*/
public class ConcretePrototypeA implements Cloneable{
private Integer id;
private String name;
private List hobby;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List getHobby() {
return hobby;
}
public void setHobby(List hobby) {
this.hobby = hobby;
}
@Override
public String toString() {
return "ConcretePrototypeA{" +
"id=" + id +
", name='" + name + '\'' +
", hobby=" + hobby +
'}';
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
创建Client类:
public class Client {
public Object startClone(ConcretePrototypeA concretePrototype){
try {
return concretePrototype.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return null;
}
}
测试类:
public class shallowTest {
public static void main(String[] args) {
//创建一个需要克隆的对象
ConcretePrototypeA c1=new ConcretePrototypeA();
List list=new ArrayList<>();
list.add("listlistlist");
//设置属性
c1.setId(1);
c1.setName("xxbb");
c1.setHobby(list);
System.out.println(c1);
//开始克隆
Client client=new Client();
ConcretePrototypeA c2= (ConcretePrototypeA) client.startClone(c1);
System.out.println(c2);
//c1和c2的对比
System.out.println("c1和c2的地址是否相同:"+(c1==c2));
System.out.println("c1和c2的list属性hobby的地址是否相同:"+(c1.getHobby()==c2.getHobby()));
System.out.println("c1和c2的name属性的地址是否相同:"+(c1.getName()==c2.getName()));
//修改c1中的hobby对象
list.add("hobbyhobby");
System.out.println(c1);
System.out.println(c2);
}
}
结果:
可以看到hobby属性的引用地址是相同的,意味着复制的不是值,而是引用的地址。即对于引用数据类型来说,复制的是原对象属性的地址,当我们对其中一个对象的属性内容进行修改时,另一个对象的该属性内容也会相应的改变,这就是浅克隆。浅克隆中所有的引用对象都仍然指向原来的对象
三、深克隆
使用序列化和反序列化的方式进行复制,需要实现序列化接口Serializable。此处修改ConcretePrototypeA类的克隆方法来演示深克隆
@Override
public Prototype clone() {
try {
ByteArrayOutputStream bos=new ByteArrayOutputStream();
ObjectOutputStream oos=new ObjectOutputStream(bos);
oos.writeObject(this);
ByteArrayInputStream bis=new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois=new ObjectInputStream(bis);
return (Prototype)ois.readObject();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
结果: