首页 > 代码库 > 设计模式之原型模式

设计模式之原型模式

  原型模式是一种创建型设计模式,它允许一个对象再创建另一个可定制的对象,根本无需知道任何创建的细节。

  定义:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。

  问题描述:创建一个重复的结构复杂的对象时的成本比较大,比如细胞分裂、新建一个模板等。

  解决方案:将这个结构复杂的对象作为一个原型,然后通过复制这个原型对象来创建更多同类型的对象。

  结构图

技术分享

  说明:

  (1)抽象原型类(Prototype):这是一个抽象角色,可用接口或抽象类来实现,这里声明了一个克隆的方法,是所有具体原型类共同拥有的父类或接口;

  (2)具体原型类:继承或实现抽象原型类的克隆方法,返回自己的一个克隆对象;

  (3)使用者(User):让一个已有的原型对象通过调用其克隆方法,获得该对象的克隆对象。

 

  举个栗子:讲一个孙悟空拔毛变小猴儿的故事,将孙悟空作为一个原型对象,克隆出不同的小猴儿,使用原型模式来实现。

  1. 新建一个原型类MonkeyPrototype,实现Cloneable接口中的clone()方法,返回自己的一个克隆对象,其中引用了Address对象。代码如下:

         技术分享

         技术分享

  2. 在类PrototypeFragment中使用一个已有的原型对象,通过调用其克隆方法获得该对象的克隆对象,并对克隆对象的名字name和地址address进行更改。代码如下:

         技术分享

  4. 运行后的效果,如图所示:

         技术分享

  上面的实现方式叫作浅克隆,它只能克隆对象本身,对于依附于对象的对象则不予克隆,只对其地址克隆。栗子中,Address就是依附于原型类MonkeyPrototype的对象的对象,只克隆了它的引用地址,当克隆对象monkey_A对地址做出改动时,比如将“花果山”改为“峨眉山”,原型对象和克隆对象的地址都发生了改变。也就是说,如果原型对象的成员变量是值类型,则进行复制,不会互相影响;如果是引用类型,比如Address,则将其地址复制,实质上指向的是同一个对象。

  浅克隆需要注意的地方:

  (1)被克隆的对象需要实现需要实现Cloneable;

  (2)要重写clone() 方法;

  (3)对于依附于被克隆对象的对象,只克隆其地址。一旦更改都会改变。

  下面用深克隆来实现原型模式。具体实现方式如下:

  1. 让原型类MonkeyPrototype实现可序列化接口Serializable,增加一个deepClone()方法。代码如下:

         技术分享

  2. 在类PrototypeFragment中仍然使用这个已有的原型对象,通过调用deepClone()方法对该对象的进行深克隆,并对克隆对象的名字name和地址address进行更改。代码如下:

         技术分享

  3. 运行后的效果,如图所示:

         技术分享

  由上可知,深克隆利用了IO流进行存储,然后进行读取,不仅对克隆对象进行克隆,而且对其依附着的对象也进行了克隆。如栗子所示,原型猴儿携带的信息包含孙悟空、六耳猕猴和峨眉山,深克隆后的对象是猴儿B,我更改了名字和地址,其中名字name是值类型成员变量,则复制后改变不会影响原型对象的值,地址address是引用类型成员变量,因为是深克隆,所以也复制了一份给克隆对象,所以更改后也将不会影响到原型对象中的地址值。

  深克隆,无论原型对象的成员变量是值类型还是引用类型,都将复制一份给克隆对象。

  深克隆需要注意的地方:

  (1)利用IO流进行存储,然后进行读取;

  (2)需要实现可序列化接口Serializable。

 

  优点

  (1)当创建一个结构复杂的对象时,原型模式可以简化其创建过程,通过复制已有的实例来获得相同的对象;

  (2)简化创建结构,不需要专门的工厂存在,仅需要实现克隆方法即可。

  缺点

  (1)必须实现Cloneable接口;

  (2)逃避构造函数的约束;

  (3)克隆方法在类的内部,加重类的职责,当需要对已有类进行改造时需要修改原型类。

  使用场景

  (1)当创建成本比较大的时候,新的对象可以使用原型模式来获得;

  (2)系统要保存对象的状态,并且对象本身占用内存很少,比如要恢复到某个时间的某种状态,可以使用原型模式配合备忘录模式来实现。

设计模式之原型模式