首页 > 代码库 > 创建者模式之原型模式

创建者模式之原型模式

原型模式: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@2a139a552 多利:Sun Jan 04 01:04:03 CST 19703 原型模式.Ship@70dea4e4 多利的儿子: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@2a139a552 多利:Sun Jan 04 01:04:03 CST 19703 原型模式.Ship@70dea4e4 多利的儿子: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的),是用原型模式更快的构建对象。

创建者模式之原型模式