概述
原型模式(Prototype Pattern)是指使用原型实例创建对象的类型,并且通过复制原型创建新的对象,属于创建型模式不属于23中设计模式。说白了就是将一个对象作为原型,对其进行复制、克隆,产生一个和原对象类似的新对象。原型模式的例子:BeanUtils、JSON.parseObject等
原型模式适用场景:
- 类初始化消耗大
- new对象时需要非常繁琐的过程(数据准备、访问权限等)
- 构造函数比较复杂
对对象复制和拷贝分为浅复制和深复制下面来介绍
浅复制
完整复制值类型的数据,不复制引用类型的数据。换句话说就是只复制当前对象的值数据,对于内部数组、引用对象不复制,也就是说修改引用对象时,所以复制对象包括原型对象都会被修改。
下面我们实现JDK的Cloneable接口实现克隆
原型实体对象
public class ConcretePrototypeA implements Cloneable{
private String name;
private String age;
private Date birthday;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
@Override
public String toString() {
return "ConcretePrototypeA{" +
"name='" + name + '\'' +
", age='" + age + '\'' +
", birthday=" + birthday +
'}';
}
@Override
public Object clone() throws CloneNotSupportedException {
Object obj = super.clone();
ConcretePrototypeA cloneObj = (ConcretePrototypeA)obj;
return obj;
}
}
使用Object的clone方法实现
测试代码
public class Client {
public static void main(String[] args) throws CloneNotSupportedException {
ConcretePrototypeA concretePrototypeA1 = new ConcretePrototypeA();
concretePrototypeA1.setAge("20");
concretePrototypeA1.setName("小明");
concretePrototypeA1.setBirthday(new Date(21322212L));
System.out.println("原型对象"+concretePrototypeA1);
ConcretePrototypeA concretePrototypeA2 = (ConcretePrototypeA) concretePrototypeA1.clone();
System.out.println("复制对象"+concretePrototypeA2);
System.out.println("原型对象与复制对象是否相等:"+ (concretePrototypeA1 == concretePrototypeA2));
System.out.println("原型对象Birthday与复制对象Birthday是否相等:"+(concretePrototypeA1.getBirthday() == concretePrototypeA2.getBirthday()));
concretePrototypeA2.getBirthday().setTime(55455544L);
System.out.println("原型对象"+concretePrototypeA1);
System.out.println("复制对象"+concretePrototypeA2);
}
}
结果

可以看到复制了一个新的对象,复制了值类型的属性,不过Date类型的Birthday属性没有复制仍然是同一个引用,我们修改复制对象的Birthday属性值时,元对象也会改变
深复制
除了复制值类型的属性,那些指向对象的引用也会复制指向一个新的对象。那么这是怎么实现的呢。我们举一个经典的例子齐天大圣。首先他是只猴子,手拿金箍棒,可变大小,一根猴毛吹出千万个泼猴。
原型猴子Monkey
public class Monkey {
public String height;
public String weight;
public Date birthday;
}
引用类金箍棒jinGuBang
public class JinGubang implements Serializable {
public float h = 100;
public float d = 10;
public void big()
{
this.h *= 2;
this.d *=2;
}
public void small()
{
this.h = this.h/2;
this.d = this.d/2;
}
}
具体原型类QiTianDaShen
public class QiTianDaSheng extends Monkey implements Cloneable, Serializable {
public JinGubang jingubang;
public QiTianDaSheng()
{
this.height = "1.8m";
this.weight = "130g";
this.birthday = new Date();
this.jingubang = new JinGubang();
}
@Override
protected Object clone() throws CloneNotSupportedException {
return this.deepClone();
}
public Object deepClone()
{
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
QiTianDaSheng copy = (QiTianDaSheng)ois.readObject();
copy.birthday = new Date();
bos.close();
oos.close();
bis.close();
ois.close();
return copy;
}
catch (Exception e)
{
e.printStackTrace();
return null;
}
}
public Object shallowClone() throws CloneNotSupportedException {
return super.clone();
}
}
测试类
public class DeepTest {
public static void main(String[] args) {
QiTianDaSheng q1 = new QiTianDaSheng();
try {
QiTianDaSheng q2 = (QiTianDaSheng)q1.clone();
System.out.println("原型对象与复制对象里面的引用对象是否相等:"+(q1.jingubang == q2.jingubang));
}catch (Exception e)
{
e.printStackTrace();
}
}
}
结果

实现序列化接口,把对象写到二进制流里然后在读出对象产生新的对象实现深度克隆
有没有发现原型模式与单例模式是互相冲突的,所以为防止克隆破坏单例时,我们禁止深度克隆。要么单例类不实现Cloneable接口;要么重写clone方法,在clone方法里直接返回单例对象就行。
参考:https://en.wikipedia.org/wiki/Prototype_pattern
来源:https://www.cnblogs.com/gudazhi/p/10601134.html