Prototype
别名:克隆( Clone)
定义
原型实例指定创建对象的种类,并且通过复制这些原型创建新的对象。
核心:复制原型对象。以系统中已存在的一个对象为原型,直接基于内存二进制流进行复制,不需要再经历耗时的对象初始化过程,使创建时间缩短。
应用场景
1.当需要复制一些对象,同时又希望代码独立于这些对象所属的具体类。
该情况通常出现在代码需要处理第三方代码(通过接口传递)的对象时。即使不考虑代码耦合的情况下,也不能依赖这些对象所属的具体类,因为不清楚它们的具体信息。
2.如果子类的区别仅在于其对象的初始化方式,那么可以该模式来减少子类的数量。别人创建这些子类的目的的可能是为了创建特定类型的对象。
在该模式中,可以使用一系列预生成的、各种类型的对象作为原型。【客户端不必根据需求对子类进行实例化,只需找到合适的原型并对其进行克隆即可】
实现方法
1.创建原型接口,并在其中声明 克隆 方法。如果你已有类层次结构,则只需在其所有类中添加该方法即可。
2.原型类必须另行定义一个以该类对象为参数的构造函数。构造函数必须复制参数对象中的所有变量值到新建实体中。当需要修改子类时,则必须调用父类构造函数,让父类复制其私有成员变量值【若语言不支持重载时,则需要使用特定的方法进行复制】
3.克隆方法通常只有一行代码: new 运算符调用原型版本的构造函数。【每个类都必须显式重写克隆方法并使用自身类名调用new运算符。否则,克隆方法可能会生成父类的对象】
4.可以创建一个中心化原型注册表,用于存储常用原型
探讨浅拷贝和深拷贝问题
浅拷贝
对于基本类型和String类型的成员变量,浅拷贝会直接进行值传递。
对于引用数据类型的成员变量,浅拷贝会进行引用传递(传地址)。则两个对象都会指向同一个实例。在这种情况下,一个对象修改该成员变量会对另一个对象产生影响。
实现方法:super.clone()默认的clone方法
代码的问题展示
拷贝对象
public class CloneAbleTarget implements Cloneable {
public String name;
public int age;
public CloneAbleTarget cloneAbleTarget;
public CloneAbleTarget(String name, int age) {
this.name = name;
this.age = age;
}
public CloneAbleTarget(String name, int age, CloneAbleTarget cloneAbleTarget) {
this.name = name;
this.age = age;
this.cloneAbleTarget = cloneAbleTarget;
}
@Override
public String toString() {
return "CloneAbleTarget{" +
"name='" + name + ''' +
", age=" + age +
", cloneAbleTarget=" + cloneAbleTarget +
'}';
}
@Override
protected Object clone() {
CloneAbleTarget cloneAbleTarget = null;
try {
cloneAbleTarget = (CloneAbleTarget) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return cloneAbleTarget;
}
}
test
public class Client {
public static void main(String[] args) {
CloneAbleTarget c1 = new CloneAbleTarget("C1",22,new CloneAbleTarget("C1_1",12));
CloneAbleTarget c2 = (CloneAbleTarget) c1.clone();
System.out.println(c1);//{name='C1', age=22, cloneAbleTarget=CloneAbleTarget{name='C1_1', age=12, cloneAbleTarget=null}}
System.out.println(c2);//{name='C1', age=22, cloneAbleTarget=CloneAbleTarget{name='C1_1', age=12, cloneAbleTarget=null}}
//此时修改c2的相关属性
c2.name="C2";
c2.age = 11;
c2.cloneAbleTarget.name="C2_2";
c2.cloneAbleTarget.age=13;
//进行输出 查看哪些成员变量变了
System.out.println(c1); //{name='C1', age=22, cloneAbleTarget=CloneAbleTarget{name='C2_2', age=13, cloneAbleTarget=null}}
System.out.println(c2); //{name='C2', age=11, cloneAbleTarget=CloneAbleTarget{name='C2_2', age=13, cloneAbleTarget=null}}
/*
从结果对比可知 c2修改了引用类型,c1的引用类型也随之变化。【地址传递】
*/
}
}
深拷贝
拷贝对象的所有基本类型,并为所有的引用类型变量申请存储空间,并复制每个成员变量所引用的对象。
实现方式
1.重写clone方法来实现深拷贝
引用类型类
public class DeepCloneabeTarget implements Cloneable {
private String cloneName;
private String cloneClass;
public DeepCloneabeTarget(String cloneName, String cloneClass) {
this.cloneName = cloneName;
this.cloneClass = cloneClass;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
复制类
public class DeepProtoType implements Cloneable {
private String name;
private DeepCloneabeTarget deepCloneabeTarget;
public DeepProtoType(String name, DeepCloneabeTarget deepCloneabeTarget) {
this.name = name;
this.deepCloneabeTarget = deepCloneabeTarget;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public DeepCloneabeTarget getDeepCloneabeTarget() {
return deepCloneabeTarget;
}
public void setDeepCloneabeTarget(DeepCloneabeTarget deepCloneabeTarget) {
this.deepCloneabeTarget = deepCloneabeTarget;
}
@Override
public String toString() {
return "DeepProtoType{" +
"name='" + name + ''' +
", deepCloneabeTarget=" + deepCloneabeTarget +
'}';
}
@Override
protected Object clone() throws CloneNotSupportedException {
Object deep = null;
//复制基本类型+String类型
deep = super.clone();
//对引用类型,单独处理
DeepProtoType deepProtoType = (DeepProtoType) deep;
deepProtoType.deepCloneabeTarget = (DeepCloneabeTarget) deepCloneabeTarget.clone();
return deepProtoType;
}
}
test
public class Client {
public static void main(String[] args) throws CloneNotSupportedException {
DeepProtoType deep1 = new DeepProtoType("Tom",new DeepCloneabeTarget("Name_clone","Class_clone"));
DeepProtoType deep2 = (DeepProtoType) deep1.clone();
System.out.println(deep1);//{name='Tom', deepCloneabeTarget=com.rz.prototype.DeepCloneabeTarget@3d075dc0}
System.out.println(deep2);//{name='Tom', deepCloneabeTarget=com.rz.prototype.DeepCloneabeTarget@214c265e}
/*
从结果上看 对象和复制对象的引用类型成员变量的地址不同。则深拷贝成功
*/
}
}
2.通过对象序列化实现深拷贝
引用类型类
public class DeepCloneabeTarget implements Serializable{
private String cloneName;
private String cloneClass;
public DeepCloneabeTarget(String cloneName, String cloneClass) {
this.cloneName = cloneName;
this.cloneClass = cloneClass;
}
@Override
public String toString() {
return "DeepCloneabeTarget{" +
"cloneName='" + cloneName + ''' +
", cloneClass='" + cloneClass + ''' +
'}';
}
}
复制类
public class DeepProtoType implements Serializable,Cloneable {
private String name;
private DeepCloneabeTarget deepCloneabeTarget;
public DeepProtoType(String name, DeepCloneabeTarget deepCloneabeTarget) {
this.name = name;
this.deepCloneabeTarget = deepCloneabeTarget;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public DeepCloneabeTarget getDeepCloneabeTarget() {
return deepCloneabeTarget;
}
public void setDeepCloneabeTarget(DeepCloneabeTarget deepCloneabeTarget) {
this.deepCloneabeTarget = deepCloneabeTarget;
}
@Override
public String toString() {
return "DeepProtoType{" +
"name='" + name + ''' +
", deepCloneabeTarget=" + deepCloneabeTarget +
'}';
}
@Override
protected Object clone(){
//创建流对象
ByteArrayOutputStream bos = null;
ObjectOutputStream oos = null;
ByteArrayInputStream bis = null;
ObjectInputStream ois = null;
try {
//序列化
bos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(bos);
oos.writeObject(this); //使这个对象以对象流的方式输出
//反序列化
bis = new ByteArrayInputStream(bos.toByteArray());
ois = new ObjectInputStream(bis);
DeepProtoType copyObj = (DeepProtoType) ois.readObject();
return copyObj;
} catch (Exception e) {
e.printStackTrace();
return null;
} finally {
//关闭流
try {
ois.close();
bis.close();
bos.close();
oos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
test
public class Client {
public static void main(String[] args) throws CloneNotSupportedException {
DeepProtoType deep1 = new DeepProtoType("Tom",new DeepCloneabeTarget("Name_clone","Class_clone"));
DeepProtoType deep2 = (DeepProtoType) deep1.clone();
System.out.println(deep1+" "+deep1.getDeepCloneabeTarget().hashCode());
System.out.println(deep2+" "+deep2.getDeepCloneabeTarget().hashCode());
/*
DeepProtoType{name='Tom', deepCloneabeTarget=DeepCloneabeTarget{cloneName='Name_clone', cloneClass='Class_clone'}} 1650967483
DeepProtoType{name='Tom', deepCloneabeTarget=DeepCloneabeTarget{cloneName='Name_clone', cloneClass='Class_clone'}} 1604839423
从结果上看 对象和复制对象的引用类型成员变量的地址不同。则深拷贝成功
*/
}
}
优缺点
优点:
1.可以克隆对象,而无需与它们所属的具体类相耦合。运行时可以增加和删除产品
2.可以克隆预生成原型,避免反复运行初始化代码,得以改变值以指定新对象
3.更方便地生成复杂对象,可以用类动态配置应用
4.可以用继承以外的方式来处理复杂对象的不同配置
缺点:
1.需要为每个类都配置克隆方法
2.克隆方法处于类的内部,当对已有类进行改造的时候,需要修改代码,违背了开闭眼则。
结构
UML图
classDiagram
Client-->Prototype
Prototype<|..ConcretePrototype
ConcretePrototype<|..SubclassPrototype
class Prototype{
<<interface>>
+clone() Prototype
}
class ConcretePrototype{
-field1
+ConcretePrototype(prototype)
+clone() Prototype
}
class SubclassPrototype{
-field2
+SubclassPrototype(prototype)
+clone():Prototype
}
参与者
1.原型( Prototype ) :接口将对克隆方法进行声明。
2.具体原型( Concrete Prototype ) : 类将实现克隆方法。除了将原始对象的数据复制到克隆题中之外,该方法有时还需处理克隆过程中的极端情况,例如克隆关联对象和梳理递归以来等等
3.客户端( Client ) :可以复制实现了原型接口的任何对象
通用写法
public interface Prototype <T>{
T clone();
}
public class ConcretePrototypeA implements Prototype<ConcretePrototypeA>{
public String desc;
public ConcretePrototypeA(String desc){
this.desc = desc;
}
@Override
public ConcretePrototypeA clone() {
return new ConcretePrototypeA(this.desc);
}
@Override
public String toString() {
return "ConcretePrototypeA{" +
"desc='" + desc + ''' +
'}';
}
}
public class Client {
public static void main(String[] args) {
ConcretePrototypeA prototypeA = new ConcretePrototypeA("Hello");
System.out.println(prototypeA);
ConcretePrototypeA clone = prototypeA.clone();
clone.desc = "wow";
System.out.println(clone);
}
}