原型模式

110 阅读6分钟

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);
  }
}