zoukankan      html  css  js  c++  java
  • 创建者模式之原型模式

    原型模式:Prototype原型模式是一种创建型设计模式,Prototype模式允许一个对象再创建另外一个可定制的对象,根本无需知道任何如何创建的细节,工作原理是:通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它们自己来实施创建。

    实际上就是通过对原对象的一个拷贝,作为一个新对象,改变原来对象的一些属性,得到新的对象的过程,使用的原理就是java中的clone方法(c#里面也有一个clone方法,其他语言不太懂。c/c++应该是一个内存拷贝吧)。

    看下面一段代码:

    声明一个Ship类作为我们可以colne的类,因此继承Cloneable接口。代码如下:

    import java.io.Serializable;
    import java.util.Date;
    
    public class Ship implements Cloneable, Serializable {
        public String name;
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        private Date birthDay;
        
        
        public Date getBirthDay() {
            return birthDay;
        }
    
        public void setBirthDay(Date birthDay) {
            this.birthDay = birthDay;
        }
    
        public Object clone() throws CloneNotSupportedException {
            Object o = super.clone();
            return o;
        }
        
        public Ship(String name, Date birthDay){
            super();
            this.name = name;
            this.birthDay = birthDay;
        }
    }

    然后看一下使用类里面如何调用:

    import java.util.Date;
    
    public class Main {
        public static void main(String[] args) throws CloneNotSupportedException {
            Date date = new Date(123445543L);
            Ship s1 = new Ship("多利", date);
            
            System.out.println(s1);
            System.out.println(s1.getName() + ":" + s1.getBirthDay());
            
            date.setTime(234243234L);
            
            Ship s2 = (Ship)s1.clone();
            s2.setName("多利的儿子");
            System.out.println(s2);
            System.out.println(s2.getName() + ":" + s2.getBirthDay());
        }
    }

    这样执行下来的结果是

    原型模式.Ship@2a139a55
    多利:Fri Jan 02 18:17:25 CST 1970
    原型模式.Ship@70dea4e
    多利的儿子:Sun Jan 04 01:04:03 CST 1970

    说明已经不是一个类了,但是里面的参数是一样的(除了自己修改的),这样就能快速的得到一个新的对象,而不是通过new方法。

    但是这里会有一个问题,就是浅拷贝和深拷贝的问题,这里是一个浅拷贝,也就是s1和s2使用的一个Date对象,他们复制的时候,复制的是引用,指向的地址还是一样的。如果调用改成这样:

     1 public static void main(String[] args) throws CloneNotSupportedException {
     2         Date date = new Date(123445543L);
     3         Ship s1 = new Ship("多利", date);
     4         Ship s2 = (Ship)s1.clone();
     5         
     6         date.setTime(234243234L);
     7         System.out.println(s1);
     8         System.out.println(s1.getName() + ":" + s1.getBirthDay());
     9         
    10         s2.setName("多利的儿子");
    11         System.out.println(s2);
    12         System.out.println(s2.getName() + ":" + s2.getBirthDay());
    13     }

    输出是这样的:

    1 原型模式.Ship@2a139a55
    2 多利:Sun Jan 04 01:04:03 CST 1970
    3 原型模式.Ship@70dea4e
    4 多利的儿子:Sun Jan 04 01:04:03 CST 1970

    通过这两个调用就可以看出来这是一个浅拷贝。我们下面看一下深拷贝是这么完成的。

    1 public Object clone() throws CloneNotSupportedException {
    2         Object o = super.clone();
    3         
    4         //添加如下代码实现深复制(deep Clone)
    5         Ship s = (Ship)o;
    6         s.setBirthDay((Date)this.birthDay.clone());//把属性也进行克隆!
    7         
    8         return o;
    9     }

    改变clone方法,添加了两行代码,再来看一下结果

    1 原型模式.Ship@2a139a55
    2 多利:Sun Jan 04 01:04:03 CST 1970
    3 原型模式.Ship@70dea4e
    4 多利的儿子:Fri Jan 02 18:17:25 CST 1970

    两个时间已经不同了,说明在给s1设置时间的时候,没有对s2的Date对象改变,也就是s2和s1的Date指向的是不同的内容。这就实现了深拷贝。

    其实关于深拷贝的方法还可以使用序列化来实现,要想让我们的类(Ship类)可以序列化,必须继承Serializable接口。我们来看一下

     1 public class Main2 {
     2     public static void main(String[] args) throws CloneNotSupportedException, IOException, ClassNotFoundException {
     3         Date date = new Date(123445543L);
     4         Ship s1 = new Ship("多利", date);
     5                 
     6         ByteArrayOutputStream bos = new ByteArrayOutputStream();
     7         ObjectOutputStream      oos = new ObjectOutputStream(bos);
     8         oos.writeObject(s1);
     9         byte[] bytes = bos.toByteArray();
    10         
    11         ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
    12         ObjectInputStream ois = new ObjectInputStream(bis);
    13             
    14         Ship s2 = (Ship) ois.readObject();
    15         
    16         date.setTime(234243234L);
    17         System.out.println(s1);
    18         System.out.println(s1.getName() + ":" + s1.getBirthDay());
    19         
    20         s2.setName("多利的儿子");
    21         System.out.println(s2);
    22         System.out.println(s2.getName() + ":" + s2.getBirthDay());
    23     }
    24 }

    上面的代码使用序列化将s1对象保存在了byte数组中,然后将byte数组的数据反序列化得到对象赋值给s2。改变了s1的时间之后,s2的时间并没有发生变化,说明这是一个深复制的过程。

    最后说一下原型模式的意义是什么呢?其实为了解决构造函数非常复杂,构造需要很长时间的时候,我们可以使用原型模式节省大量的时间。如果我们在一个类的构造函数里面设置一个Sleep(10),构造1000次(表示这个构造函数非常复杂)。使用一般的new和我们的原型模式产生新的对象来进行比较的话,会发现原型模式非常快,而一般的new非常的慢。

    除此之外,在Spring中,构造函数的方法只有两种,一个是单例,一个是原型模式。在工厂模式中,不是用new生产对象(单例还是需要new的),是用原型模式更快的构建对象。

  • 相关阅读:
    PowerCat DNS 隧道通信
    各种反弹shell方法总结备忘
    Halo-个人独立博客系统
    内网渗透之域渗透
    使用 EW 作Socks5代理内网穿透
    PowerShell攻击:nishang
    贝叶斯网络
    Anaconda的CondaHTTPError问题
    完美解决win10系统无法安装.NET Framework问题
    敏捷开发中如何做质量管理?
  • 原文地址:https://www.cnblogs.com/worsun/p/5876487.html
Copyright © 2011-2022 走看看