设计模式之(六)原型模式(ProtoType)

认识原型模式  

  原型模式是比较简单的设计模式。废话不多说,直接看定义:用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象。通过实例指定种类,种类就是初始化的类,然后通过拷贝创建对象。先展示一个实现的原型模式的例子

 1 public class Product {
 2     
 3     private String proID;
 4     
 5     private String  proName;
 6     
 7     private String proDescption;
 8     
 9     public String getProID() {
10         return proID;
11     }
12 
13     public void setProID(String proID) {
14         this.proID = proID;
15     }
16 
17     public String getProName() {
18         return proName;
19     }
20 
21     public void setProName(String proName) {
22         this.proName = proName;
23     }
24 
25     public String getProDescption() {
26         return proDescption;
27     }
28 
29     public void setProDescption(String proDescption) {
30         this.proDescption = proDescption;
31     }
32     
33     public void showPro(){
34         System.out.println("ID:"+this.getProID()+";Name:"+this.getProName()+";Descrip:"+this.getProDescption());
35         System.out.println("-------------------------------------");
36     }
37     
38     //关键方法
39     public Product cloneself(){
40         Product p = new Product();
41         p.setProName(this.proID);
42         p.setProID(this.proID);
43         p.setProDescption(this.proDescption);
44         
45         return p;
46     }
47 }
48 
49 public class Client {
50     public static void main(String[] args) {
51         Product pp = new Product();
52         pp.setProID("P0001");
53         pp.setProName("玩具一号");
54         pp.setProDescption("此为玩具一号的初代产品");
55         
56         pp.showPro();
57         
58         //拷贝
59         Product pp2 = pp.cloneself();
60         pp2.showPro();
61         pp2.setProDescption("此为玩具一号的升级产品");
62         pp2.showPro();
63         
64         //原来的
65         pp.showPro();
66     }
67 }
68 
69 //实行效果
70 ID:P0001;Name:玩具一号;Descrip:此为玩具一号的初代产品
71 -------------------------------------
72 ID:P0001;Name:P0001;Descrip:此为玩具一号的初代产品
73 -------------------------------------
74 ID:P0001;Name:P0001;Descrip:此为玩具一号的升级产品
75 -------------------------------------
76 ID:P0001;Name:玩具一号;Descrip:此为玩具一号的初代产品
77 -------------------------------------

  通过例子可以看出来,原型模式的核心就是克隆自己的方法,在例子中就是 cloneself,实例对象调用的 cloneself ,就识别出来了对象是哪个类的。同时拷贝了一个新的对象,和原来的实例是完全分离的两个实例。这样实现的原型模式 和 new 比起来有诸多优势,

        1)通过拷贝创建对象时,不用关心实例来自哪个类型,只要在例子中实现了拷贝自己的方法即可。

   2)通过 new 新建对象时,每个值都是初始状态的,对各个属性,还需要赋值处理。而原型模式在新建了对象时候,就直接获取了原来对象的值,如果有个别属性不一样修改了即可。

       通过上面那种方式实现的原型模式,只是在编程方面更灵活。编程效率更高而已,对于执行效果而言,和不用效果是一样的。因为这样实现的模式也是通过new来创建的。有没有更好的克隆方式呢,java 语言里面是有这样的方式的。接下来就介绍 java 实现的 clone 方式。

public class ProtoType implements Cloneable {

    //......属性值省略
    
    @Override
    public ProtoType clone(){
        ProtoType pro = null;
        try{
            pro = (ProtoType)super.clone();
        }
        catch(CloneNotSupportedException ex){
            ex.printStackTrace();
        }
        return pro;
    }
}

  上面的代码就是用 java 自己的 clone 实现的克隆。方式就是在 Object 里面有一个 clone 方法,就是用来拷贝对象的,只是使用这个函数需要实现 Cloneable 接口,然后再重写这个方法。为了更好的理解 clone 用法,我们来改造上面的例子。通过 java 语言提供的 clone 来完成这个例子。只是把关键代码贴出来,就是把 原来例子中的 cloneself() 替换成 clone() 。

用 java 自带 clone 改造例子

 1 public class Product implements Cloneable {
 2     
 3     private String proID;
 4     
 5     private String  proName;
 6     
 7     private String proDescption;
 8     
 9     public String getProID() {
10         return proID;
11     }
12 
13     public void setProID(String proID) {
14         this.proID = proID;
15     }
16 
17     public String getProName() {
18         return proName;
19     }
20 
21     public void setProName(String proName) {
22         this.proName = proName;
23     }
24 
25     public String getProDescption() {
26         return proDescption;
27     }
28 
29     public void setProDescption(String proDescption) {
30         this.proDescption = proDescption;
31     }
32     
33     public void showPro(){
34         System.out.println("ID:"+this.getProID()+";Name:"+this.getProName()+";Descrip:"+this.getProDescption());
35         System.out.println("-------------------------------------");
36     }
37     
38     //关键方法
39     //    public Product cloneself(){
40     //        Product p = new Product();
41     //        p.setProName(this.proID);
42     //        p.setProID(this.proID);
43     //        p.setProDescption(this.proDescption);
44     //        
45     //        return p;
46     //    }
47     
48     //关键方法
49     @Override
50     public Product clone(){
51         Product pro = null;
52         try{
53             pro = (Product)super.clone();
54         }
55         catch(CloneNotSupportedException ex){
56             ex.printStackTrace();
57         }
58         
59         return pro;
60     }
61 }

   通过 java 语言提供的 clone 方法来复制效率很高,主要有一下原因:

           1)不用执行构造函数。

      2)原型模式是在内存二进制流的拷贝,要比直接new一个对象性能好很多,特别是要在一个循环体内产生大量的对象时,原型模式可以更好地体现其优点。

 浅拷贝和深拷贝

  浅拷贝:就是只拷贝基本类型的值,比如 int、String 等类型。(这里把 String 类型当成了基本类型)。

       深拷贝:除了浅拷贝的值外,还需要拷贝引用类型的对象。

 1 public class Product2 implements Cloneable {
 2     private String name;
 3     
 4     private ArrayList<String> list = new ArrayList<String>();
 5     
 6     public String getName() {
 7         return name;
 8     }
 9 
10     public void setName(String name) {
11         this.name = name;
12     }
13 
14     public ArrayList<String> getList() {
15         return list;
16     }
17 
18     public void setList(String str) {
19         this.list.add(str);
20     }
21     
22     @Override
23     public Product2 clone(){
24         Product2 pro = null;
25         try{
26             pro = (Product2)super.clone();
27         }
28         catch(CloneNotSupportedException ex){
29             ex.printStackTrace();
30         }
31         
32         return pro;
33     }
34     
35     public void show(){
36         System.out.println("name="+this.name+"   list="+this.list);
37     }
38 }
39 
40 public class Client {
41     public static void main(String[] args) {
42         Product2 pp = new Product2();
43         pp.setName("pro2");
44         pp.setList("pwq");
45         pp.show();
46         
47         Product2 pp2 = pp.clone();
48         pp2.setName("pro222");
49         pp2.setList("pcf");
50         pp2.show();
51     }
52 }
53 
54 /*****执行效果**********/
55 name=pro2   list=[pwq]
56 name=pro222   list=[pwq, pcf]

     比较两次的执行效果,克隆后的类名字是分离了,就是成功克隆了;但是 list 这个很明显就是共享了,就是没有克隆成功。这个就是浅克隆。如果需要深克隆,就需要修改下上面代码;

 1 @SuppressWarnings("unchecked")
 2     @Override
 3     public Product2 clone(){
 4         Product2 pro = null;
 5         try{
 6             pro = (Product2)super.clone();
 7             pro.list = (ArrayList<String>)pro.list.clone();
 8         }
 9         catch(CloneNotSupportedException ex){
10             ex.printStackTrace();
11         }
12         
13         return pro;
14     }

  加上这句话后,修改为深度拷贝。手动编写的类也是典型的引用类型,深拷贝也是需要重点注意的。

原型管理器

       有时候原型可能有多个,而且还不固定,中间可能动态的变化。这个时候对原型的管理就需要维护一个注册表,这个注册表就是原型管理器。在原型注册器中可以添加和销毁。看例子理解

public interface ProtoType {
    public String getName();
    
    public void setName(String name);
    
    public ProtoType clone();
}

public class ConcreteProtoTypeA implements ProtoType{
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
    
    @Override
    public ProtoType clone(){
        ConcreteProtoTypeA cA = new ConcreteProtoTypeA();
        cA.setName(this.name);
        return cA;
    }
}

public class ConcreteProtoTypeB implements ProtoType{
    //..........
}

//原型管理器
public class ProtoTypeManager {
    private static Map<String,ProtoType> pt = new HashMap<String,ProtoType>();
    //不让实例化
    private ProtoTypeManager(){}
    
    public static synchronized void setProtoType(String protoID,ProtoType p){
        pt.put(protoID, p);
    }
    
    public static synchronized void removeProtoType(String protoID){
        pt.remove(protoID);
    }
    
    public static synchronized ProtoType getProtoType(String protoID){
        ProtoType p = null;
        
        if(pt.containsKey(protoID)){
            p = pt.get(protoID);
        }
        return p;
    }
    
    public static void show(){
        System.out.println(pt);
    }
}
原型管理器

      通过这个例子就很清楚的理解了原型管理器是有什么用处了,本质上就是存放多个原型的,并且能够动态的添加、删除、获取。

使用场景

  1、需要一个类实例化大量的重复对象,或者数据重复性很大,极个别需要修改的属性。

  2、对象初始化过程比较复杂。

  3、在运行时刻不方便获取原型的类时,也可以通过原型模式来实现。

小结

  原型模式说了这么多,本质就是原型不需要获取到所属种类,通过原型就能够通过克隆自己来创建对象。通过这个本质就能看到引出的优势。而 java 又对这个模式进行了语言级别的支持。Object 的 clone 就能够克隆,只要实现了 cloneable 接口。java 自带的 clone 克隆自己还不需要再次执行构造方法,操作的是内存二进制数据,效率非常的好。

原文地址:https://www.cnblogs.com/pengweiqiang/p/10798279.html