问题描述
无论工厂方法模式,或者是抽象工厂模式,客户端程序在创建一个对象的时候,都依赖于一个具体的工厂类。本质上来说,客户程序和这些具体的工厂类紧紧的绑死了。但是,某些程序(例如框架程序)可能无法访问这些具体的工厂类,也可能需要一种动态的,可以配置的方式来创建对象。原型模式通过一个原型管理器来提供这种动态性,通过复制/克隆一个已经存在的对象来解除对于工厂类的依赖。
原型模式
如图所示,Prototype定义了一个抽象的Clone()接口,任何一个类型,只要支持了这个抽象接口,客户端都可以通过该类型的一个实例来复制/克隆出一个该类型的新的实例。
讨论
设计一个原型模式的时候,需要考虑如下问题:
1)。原型管理器:用于注册,取消注册原型对象。客户通过关键字查询原型对象。原型管理器支持原型的动态注册和取消注册,为原型模式提供了动态性和可配置性。
2)。深拷贝:在实现Clone()接口的时候,通常需要考虑深拷贝来保证原型对象和复制对象的独立性。否则,实例对象内部的资源(例如指针,文件句柄, Socket等)在程序运行中可能出现一些经典错误例如:悬挂指针的访问,指针的重复释放等问题。
3)。克隆对象的初始化:如前所述,Clone()接口适用于任何类型,不能携带任何参数。因此,在实现Clone()方法的时候,需要考虑是否提供初始化函数来初始化克隆对象。Java语言根基类Object声明了clone()方法如下,因此任何Java对象只要实现了clone()方法,就天然的支持了原型模式。
class Object extends Throwable
{
...
protected native Object clone() throws CloneNotSupportedException;
...
}
Clone()接口是否可以看作是一个特殊的工厂方法?如果拘泥于工厂方法模式的形式上看,原型模式当然不是工厂方法。但是从”如何通过一个稳定接口来创建实例对象”的角度看,原型模式可以是一种特殊的工厂方法模式。使用原型模式的客户软件依据原型管理器查找一个原型对象并调用Clone()接口来复制一个对象;使用工厂方法的客户软件依据一个具体工厂类并调用其提供的工厂方法来创建对象。原型模式不需要创建工厂方法类,也更加灵活可配置。原型模式甚至隐藏了所创建对象的抽象类型,因为所有的克隆操作都是基于Prototype接口的Clone()方法来完成的。比较起来,工厂方法模式只是提供一种隐藏所创建对象的具体类型的方法,其客户端依然需要了解所创建对象的抽象类型。从这个角度上讲,原型模式在类型隐藏方面比工厂方法模式更加的抽象。