原型模式(Prototype Pattern)是用于创建重复的对象,同时又能保证性能。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。这种模式是实现了一个原型接口,该接口用于创建当前对象的克隆。
场景
- 类初始化需要消耗非常多的资源
- 通过new 产生一个对象需要非常繁琐的数据准备或权限访问
- 一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改起值
实现
原型模式的本质就是对象拷贝,实现基于对象Clone,此处不做过多描述。
浅拷贝
浅拷贝(浅复制、浅克隆):被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。浅拷贝仅仅复制所考虑的对象,而不复制它所引用的对象。
创建Child类
public class Child implements Cloneable {
public String name;
public int age;
public Child(String name, int age) {
this.name = name;
this.age = age;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
创建Father类,浅拷贝
public class Father implements Cloneable {
public String name;
public int age;
public Child child;
public Father(String name, int age, Child child) {
this.name = name;
this.age = age;
this.child = child;
}
@Override
protected Object clone() {
try {
return super.clone()
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return null;
}
@Override
public String toString() {
return "name:" + name + ", age:" + age + ", Child name:" + child.name + ", Child age" + child.age +
", hashcode : " + hashCode() + ", Child hashcode:" + child.hashCode();
}
}
浅拷贝示例
public class SimpleClone {
public static void main(String[] args) {
Child child = new Child("小张三", 10);
Father fatherA = new Father("张三", 30, child);
System.out.println("clone前 fatherA:" + fatherA.toString());
Father fatherB = (Father) fatherA.clone();
System.out.println("clone后 fatherA:" + fatherA.toString());
System.out.println("clone后 fatherB:" + fatherB.toString());
fatherB.name = "李四";
fatherB.age = 40;
fatherB.child.name = "小李四";
fatherB.child.age = 15;
System.out.println("clone后修改信息 fatherA:" + fatherA.toString());
System.out.println("clone后修改信息 fatherB:" + fatherB.toString());
}
}
输出结果
clone前 fatherA:name:张三, age:30, Child name:小张三, Child age10, hashcode : 1627674070, Child hashcode:1360875712
clone后 fatherA:name:张三, age:30, Child name:小张三, Child age10, hashcode : 1627674070, Child hashcode:1360875712
clone后 fatherB:name:张三, age:30, Child name:小张三, Child age10, hashcode : 1625635731, Child hashcode:1360875712
clone后修改信息 fatherA:name:张三, age:30, Child name:小李四, Child age15, hashcode : 1627674070, Child hashcode:1360875712
clone后修改信息 fatherB:name:李四, age:40, Child name:小李四, Child age15, hashcode : 1625635731, Child hashcode:1360875712
从最后对 child 的输出可以看到,A 和 B 的 child 对象,实际上还是指向了统一个对象,只对对它的引用进行了传递
深拷贝
深拷贝(深复制、深克隆):被复制对象的所有变量都含有与原来的对象相同的值,除去那些引用其他对象的变量。那些引用其他对象的变量将指向被复制过的新对象,而不再是原有的那些被引用的对象。深拷贝把要复制的对象所引用的对象都复制了一遍。
创建Father类,深拷贝
public class Father implements Cloneable {
public String name;
public int age;
public Child child;
public Father(String name, int age, Child child) {
this.name = name;
this.age = age;
this.child = child;
}
@Override
protected Object clone() {
try {
Father father = (Father) super.clone();
father.child = (Child) this.child.clone();
return father;
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return null;
}
@Override
public String toString() {
return "name:" + name + ", age:" + age + ", Child name:" + child.name + ", Child age" + child.age +
", hashcode : " + hashCode() + ", Child hashcode:" + child.hashCode();
}
}
深拷贝示例
public class DeepClone {
public static void main(String[] args) {
Child child = new Child("小张三", 10);
Father fatherA = new Father("张三", 30, child);
System.out.println("clone前 fatherA:" + fatherA.toString());
Father fatherB = (Father) fatherA.clone();
System.out.println("clone后 fatherA:" + fatherA.toString());
System.out.println("clone后 fatherB:" + fatherB.toString());
fatherB.name = "李四";
fatherB.age = 40;
fatherB.child.name = "小李四";
fatherB.child.age = 15;
System.out.println("clone后修改信息 fatherA:" + fatherA.toString());
System.out.println("clone后修改信息 fatherB:" + fatherB.toString());
}
}
输出结果
clone前 fatherA:name:张三, age:30, Child name:小张三, Child age10, hashcode : 1627674070, Child hashcode:1360875712
clone后 fatherA:name:张三, age:30, Child name:小张三, Child age10, hashcode : 1627674070, Child hashcode:1360875712
clone后 fatherB:name:张三, age:30, Child name:小张三, Child age10, hashcode : 1625635731, Child hashcode:1580066828
clone后修改信息 fatherA:name:张三, age:30, Child name:小张三, Child age10, hashcode : 1627674070, Child hashcode:1360875712
clone后修改信息 fatherB:name:李四, age:40, Child name:小李四, Child age15, hashcode : 1625635731, Child hashcode:1580066828
优点
- 原型模式是在内存中二进制流的拷贝,要比new一个对象的性能要好,特别是需要产生大量对象时。
缺点
- 直接在内存中拷贝,构造函数是不会执行的,这样就减少了约束,这既是优点也是缺点
Android 中的应用
- 对象Clone()的实现
参考
书籍:《设计模式之禅》、《Android源码设计模式》
技术文章:菜鸟教程-设计模式