Java设计模式之原型模式

概论

什么是原型模式呢?用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。这个模式就叫作原型模式。原型模式属于对象创建者模式。

 

原型模式示例

 首先我们需要有一个原型。这个原型实现了Cloneable空接口。这是一个标记接口,并无任何的方法。

 1 package com.example.pattern.prototype;
 2 
 3 import lombok.*;
 4 
 5 import java.io.Serializable;
 6 import java.util.Arrays;
 7 import java.util.List;
 8 
 9 @Setter
10 @Getter
11 @NoArgsConstructor
12 @AllArgsConstructor
13 public class PrototypeClass implements Cloneable {
14 
15     private int id;
16     private char code;
17     private String name;
18 
19     private BaseDomain baseDomain;
20     private String[] array;
21 
22     private List<String> list;
23 
24 
25     @Override
26     protected PrototypeClass clone() {
27         PrototypeClass prototypeClass = null;
28 
29         try {
30             prototypeClass = (PrototypeClass) super.clone();
31         } catch (CloneNotSupportedException e) {
32             e.printStackTrace();
33 
34         }
35 
36         return prototypeClass;
37     }
38 
39 
40 }

第9行-12行:采用lambok注解,简化了简单对象中的繁琐的get,set,带所有参数的构造函数,无参构造函数。

第15-22行:定义了类型为原子类型,复杂对象,已经数组和集合的成员属性。

 

最后,我们需要增加一个场景类Client:

 1 public class Client {
 2 
 3     public static void main(String[] args) {
 4         PrototypeClass prototypeClass = new PrototypeClass();
 5         prototypeClass.setId(123);
 6         prototypeClass.setCode('A');
 7         prototypeClass.setName("Tom");
 8         prototypeClass.setBaseDomain(new BaseDomain());
 9 
10         String[] array = new String[]{"22222"};
11         prototypeClass.setArray(array);
12 
13         List<String> list = new ArrayList<String>();
14         list.add("CCC");
15         prototypeClass.setList(list);
16 
17         PrototypeClass cloneClass = prototypeClass.clone();
18 
19         cloneClass.setId(456);
20         cloneClass.setCode('B');
21         cloneClass.setName("Jack");
22 
23         System.out.println("prototypeClass.getId() :"+prototypeClass.getId());
24         System.out.println("cloneClass.getId():"+cloneClass.getId());
25 
26         System.out.println("prototypeClass.getCode() :"+prototypeClass.getCode());
27         System.out.println("cloneClass.getCode():"+cloneClass.getCode());
28 
29         System.out.println("prototypeClass.getName() :"+prototypeClass.getName());
30         System.out.println("cloneClass.getName():"+cloneClass.getName());
31 
32         String[] array2 = new String[]{"33333"};
33         cloneClass.setArray(array);
34 
35         List<String> list2 = new ArrayList<String>();
36         list.add("DDDD");
37         cloneClass.setList(list);
38 
39 
40         System.out.println("prototypeClass.getList().get(0) :"+prototypeClass.getList().get(0));
41         System.out.println("cloneClass.getList().get(0) :"+cloneClass.getList().get(0));
42 
43         System.out.println("prototypeClass.getArray()[0] :"+prototypeClass.getArray()[0]);
44         System.out.println("cloneClass.getArray()[0] :"+cloneClass.getArray()[0]);
45 
46 
47 
48     }
49 }

第17行:调用了对象的clone方法,直接产生一个对象。这就是对象的复制,而不是使用new 指令。使用对象复制的方式,不会调用构造函数。

我们先执行一下打印出来的结果:

 1 prototypeClass.getId() :123
 2 cloneClass.getId():456
 3 prototypeClass.getCode() :A
 4 cloneClass.getCode():B
 5 prototypeClass.getName() :Tom
 6 cloneClass.getName():Jack
 7 prototypeClass.getList().get(0) :CCC
 8 cloneClass.getList().get(0) :CCC
 9 prototypeClass.getArray()[0] :22222
10 cloneClass.getArray()[0] :22222

 从以上执行结果来看

①:如果成员属性为int char String类型,复制后的对象的属性的改变不会对原始对象的属性产生任何的影响。

②:如果成员属为是List集合,数组,复制后的对象的属性的改变也会和原始对象的属性产生了影响。

这是为什么呢?因为我们在原型中的拷贝方式是浅拷贝。什么是浅拷贝呢?super.clone是谁的方法呢,当然是Object方法的,因为Object是任何类的超类。而Object类提供的clone方法只是拷贝本对象,这个对象的内部成员属性包括数组,集合,引用对象都不拷贝,还是执行原生对象的内部元素地址。因此数组,集合,引用对象都是在原生对象还是拷贝对象中都是共享而存在的。这就是浅拷贝。

 

浅拷贝的对象中的成员属性还有对象的情况下, 像以上例子中的 BaseDomain。改变了同一个BaseDomian实例的属性name的情况下, 因为是同一个实例,因此也是共享的,一边都变。如果是重新new一个BaseDomain实例,重新对拷贝之后的对象复制,那是互相不干扰的。 

 

原型模式在源码中的应用

 1 public Object clone() {
 2         try {
 3             ArrayList<?> v = (ArrayList<?>) super.clone();
 4             v.elementData = Arrays.copyOf(elementData, size);
 5             v.modCount = 0;
 6             return v;
 7         } catch (CloneNotSupportedException e) {
 8             // this shouldn't happen, since we are Cloneable
 9             throw new InternalError(e);
10         }
11     }

以上是ArrayList的clone方法,我们可以产生了一个list之后,来clone一下,来简化操作,这里用到的是深拷贝。为什么是深拷贝而不是浅拷贝呢?第4-行-5行数组,修改次数再复制,拷贝之后的对象与原来的对象不再持有同一份引用,因此是深拷贝。而且Arrays.copyOf方法中是重新创建的一个新数组。

 

posted @ 2018-12-03 19:18  冰糖小城  阅读(418)  评论(0编辑  收藏  举报