• 设计模式之(六)原型模式(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 克隆自己还不需要再次执行构造方法,操作的是内存二进制数据,效率非常的好。

  • 相关阅读:
    jQuery琐碎
    jQuery文档加载事件
    微信,禁止分享页面
    级联下拉列表
    struts2 result随笔
    C++知识点(四)类与对象,结构体、联合体、枚举类
    C++知识点(三)函数
    剑指offer-二叉树
    LeetCode:Remove Element
    LeetCode:Remove Duplicates from Sorted Array
  • 原文地址:https://www.cnblogs.com/pengweiqiang/p/10798279.html
Copyright © 2020-2023  润新知