一、原型模式定义

1.原型模式是指原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象,属于创建型模式。

2.原型模式的核心在于拷贝原型对象。以系统中已存在的一个对象为原型,直接基于内存二进制流进行拷贝,无需再经历耗时的对象初始化过程(不调用构造函数),性能提升许多。当对象的构建过程比较耗时时,可以利用当前系统中已存在的对象作为原型,对其进行克隆(一般是基于二进制流的复制),躲避初始化过程,使得新对象的创建时间大大减少。

3.原型模式主要包含三个角色:

  A.客户(Client):客户类提出创建对象的请求

  B.抽象原型(Prototype):规定拷贝接口

  C.具体原型(Concrete Prototype):被拷贝的对象

4.原型模式主要适用于以下场景:

  A.类初始化消耗资源较多

  B.new产生的一个对象需要非常繁琐的过程(数据准备、访问权限等)

  C.构造函数比较复杂

  D.循环体中生产大量对象

二、原型模式的通用写法

 1 public interface IPrototype<T> {
 2     T clone();
 3 }
 4 
 5 /**
 6  * 具体需要克隆的对象
 7  */
 8 public class ConcretePrototype implements IPrototype {
 9 
10     private int age;
11     private String name;
12 
13     public int getAge() {
14         return age;
15     }
16 
17     public void setAge(int age) {
18         this.age = age;
19     }
20 
21     public String getName() {
22         return name;
23     }
24 
25     public void setName(String name) {
26         this.name = name;
27     }
28 
29     @Override
30     public ConcretePrototype clone() {
31         ConcretePrototype concretePrototype = new ConcretePrototype();
32         concretePrototype.setAge(this.age);
33         concretePrototype.setName(this.name);
34         return concretePrototype;
35     }
36 
37     @Override
38     public String toString() {
39         return "ConcretePrototype{"+
40                 "age=" + age +
41                 ", name='" + name + '\'' +
42                 '}';
43     }
44 }

 

2.jdk自带的写法

 1 /**
 2  * 具体需要克隆的对象
 3  */
 4 public class JdkConcretePrototype implements Cloneable {
 5 
 6     private int age;
 7     private String name;
 8 
 9     public int getAge() {
10         return age;
11     }
12 
13     public void setAge(int age) {
14         this.age = age;
15     }
16 
17     public String getName() {
18         return name;
19     }
20 
21     public void setName(String name) {
22         this.name = name;
23     }
24 
25     @Override
26     public JdkConcretePrototype clone() {
27         try {
28             return (JdkConcretePrototype)super.clone();
29         }catch (CloneNotSupportedException e){
30             e.printStackTrace();
31             return null;
32         }
33     }
34 
35     @Override
36     public String toString() {
37         return "JdkConcretePrototype{"+
38                 "age=" + age +
39                 ", name='" + name + '\'' +
40                 '}';
41     }
42 }

 

3.给ConcretePrototype增加一个个人爱好的属性hobbies:

 1 /**
 2  * 具体需要克隆的对象
 3  */
 4 public class JdkConcretePrototype implements Cloneable {
 5 
 6     private int age;
 7     private String name;
 8     private List<String> hobbies;
 9 
10     public int getAge() {
11         return age;
12     }
13 
14     public void setAge(int age) {
15         this.age = age;
16     }
17 
18     public String getName() {
19         return name;
20     }
21 
22     public void setName(String name) {
23         this.name = name;
24     }
25 
26     public List<String> getHobbies() {
27         return hobbies;
28     }
29 
30     public void setHobbies(List<String> hobbies) {
31         this.hobbies = hobbies;
32     }
33 
34     @Override
35     public JdkConcretePrototype clone() {
36         try {
37             return (JdkConcretePrototype)super.clone();
38         }catch (CloneNotSupportedException e){
39             e.printStackTrace();
40             return null;
41         }
42     }
43 
44     @Override
45     public String toString() {
46         return "JdkConcretePrototype{"+
47                 "age=" + age +
48                 ", name='" + name + '\'' +
49                 ", hobbies=" + hobbies +
50                 '}';
51     }
52 }
53 
54 public class JdkPrototypeTest {
55     public static void main(String[] args) {
56         //创建原型对象
57         JdkConcretePrototype prototype = new JdkConcretePrototype();
58         prototype.setName("Tom");
59         prototype.setAge(18);
60         List<String> hobbies = new ArrayList<String>();
61         hobbies.add("美术");
62         prototype.setHobbies(hobbies);
63         System.out.println(prototype);
64 
65         //拷贝原型对象
66         JdkConcretePrototype cloneType = prototype.clone();
67         cloneType.getHobbies().add("技术控");
68 
69         System.out.println("原型对象:" + prototype);
70         System.out.println("克隆对象:" + cloneType);
71     }
72 }

运行后发现,给复制后的克隆对象新增一项爱好,原型对象也发生了变化,不符合我们的预期。hobbies共用了一个内存地址,意味着复制的不是值,而是引用的地址,这即是我们常说的浅克隆。

 

4.使用序列化实现深度克隆

 1 /**
 2  * 具体需要克隆的对象
 3  */
 4 public class JdkConcretePrototype implements Cloneable, Serializable {
 5 
 6     private int age;
 7     private String name;
 8     private List<String> hobbies;
 9 
10     public int getAge() {
11         return age;
12     }
13 
14     public void setAge(int age) {
15         this.age = age;
16     }
17 
18     public String getName() {
19         return name;
20     }
21 
22     public void setName(String name) {
23         this.name = name;
24     }
25 
26     public List<String> getHobbies() {
27         return hobbies;
28     }
29 
30     public void setHobbies(List<String> hobbies) {
31         this.hobbies = hobbies;
32     }
33 
34     @Override
35     public JdkConcretePrototype clone() {
36         try {
37             return (JdkConcretePrototype)super.clone();
38         }catch (CloneNotSupportedException e){
39             e.printStackTrace();
40             return null;
41         }
42     }
43 
44     public JdkConcretePrototype deepClone(){
45         try {
46             ByteArrayOutputStream bos = new ByteArrayOutputStream();
47             ObjectOutputStream oos = new ObjectOutputStream(bos);
48             oos.writeObject(this);
49 
50             ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
51             ObjectInputStream ois = new ObjectInputStream(bis);
52 
53             return (JdkConcretePrototype)ois.readObject();
54         }catch (Exception e){
55             e.printStackTrace();
56             return null;
57         }
58     }
59 
60     @Override
61     public String toString() {
62         return "JdkConcretePrototype{"+
63                 "age=" + age +
64                 ", name='" + name + '\'' +
65                 ", hobbies=" + hobbies +
66                 '}';
67     }
68 }

 

5.克隆破坏单例模式,单例类不实现Cloneable接口

 

6.原型模式的优缺点

  A.优点

    a.性能优良,java自带的原型模式是基于内存二进制流的拷贝,比直接new一个对象性能上提升了许多

    b.可以使用深克隆方式保存对象的状态,使用原型模式将对象复制一份并将其状态保存起来,简化了创建对象的过程,以便在需要的时候使用(例如恢复到历史某一状态),可辅助实现撤销操作

  B.缺点

    a.需要为每一个类配置一个克隆方法

    b.克隆方法位于类的内部,当对已有类进行改造的时候,需要修改代码,违反了开闭原则

    c.在实现深克隆时需要编写较为复杂的代码,而且当对象之间存在多重嵌套引用时,为了实现深克隆,每一层对象对应的类都必须支持深克隆,实现起来较为麻烦。