设计模式(6)之原型模式
在软件系统中,有时候面临的产品是动态变化的,而且这个产品具有一定的等级结构,这是如果用工厂模式,则与产品等级结构平行的工厂方法类也要随着这种变化而变化,显然不大合适,那么如何封装这种动态的变化?从而使依赖与这些易变化的对象的客户程序不随着产品类的变化?
这就是这篇日志所要描述的:原型模式
原型模式:用原型实例指定创建对象的种类,并且通过拷贝这些原型的创建新的对象
举个例子:原型模式使用原型实例指定创建对象的种类,新产品的原型通常是现有全部产品建立的,这样的原型是被动的,并不参与复制他自己,一个细胞的有丝分裂,产生了两个同样的细胞,是一个扮演主动角色复制自己原型的例子,这演示了原型模式,一个细胞分裂,产生两个同样基因型的细胞,换句话说,细胞克隆了自己。
好现在我们构思一个场景:
假定我们要开发一个调色板,用户单击调色板上任意一个方块,将会返回一个对应的颜色的实例,下面我们看看如何通过原型模式来达到这个系统动态加载具体产品的目的。
很显然,我们利用OO思想,把每一个颜色作为一个对象,并为他们抽象出一个公用的父类,
代码实现:
客户需要某一种颜色的时候,只需要创建对应的具体类的实例就可以了,但是这样我们并没有达到封装变化点的目的,也许你会说,可以使用工厂方法模式,为每一个具体子类定义一个与等级平行的工厂类,好,实现代码如下:
实现这一步之后,可以看到,客户程序只要调用工厂方法就可以了,似乎我们用工厂方法模式来解决是没有问题的,但是,我们考虑的仅仅是封装了new变化,而没有考虑颜色的数量是不断变化的,甚至是在程序运行的过程中动态增加和减少的,那么用这种方法实现,随着颜色数量的不断增加,子类的数量会迅速膨胀,导致子类过多,显然用工厂方法模式有些不大合适。
进一步思考,这些color子类仅仅在初始化的颜色对象类别上有所不同,添加一个colortool这样的类来参数化他的实例,而这些实例是由color支持和创建的,我们让colortool通过克隆或这拷贝一个color子类的实例来创建新的color,这个实例就是一个原型。
代码实现:
现在我们分析一下.这样带来了什么好处?首先从子类的数目上大大减少了,不需要再为每一种具体的颜色产品而定一个类和它等级平行的工厂方法类,而colortool则扮演了原型管理器的角色。
客户端实现的程序为:
从上面的代码可以看到,客户程序通过注册原型实例就可以讲一个具体产品类并入到系统中,在运行时刻,可以动态的建立和删除原型。最后还要注一点,在上面的例子中,用的是浅表复制。如果想做深表复制,需要通过序列化的方式来实现,经过了上面的分析,我们再来考虑这个问题:
1、为什么需要原型模式?
引入原型模式的本质在于利用已经有的一个原型对象,快速的生成和原型对象一样的实例。你有一个A的实例a:A a =new A();现在你想生成和A一样的一个实例b,按照原型模式,应该是这样:A b=a.clone();而不是重新生再new一个A对象。通过上面这句话就可以得到一个和a一样的实例,确切的说,应该说,应该是他们的数据成员一样的,原型模式同样返回一个A对象而没有使用new操作。
2、引入原型模式带来了社么好处? 可以看到,引入prototype模式后我们不在需要一个具体产品等级结构平行的工厂方法类,减少了类的结构,同时客户程序可以运行时刻建立和删除原型。
3、原型模式满足了那些面向对象的设计原则?
依赖倒置原则:上面的例子。原型管理器(color Manager)仅仅依赖于抽象部分(color Prototype),而且具体实现细节(concete Color)则依赖于抽象部分(color prototype),所以prototype很好的满足了依赖倒置原则。
深度复制代码如下:
实现要点:
1、使用原型管理器,体现在一个系统中原型数目不固定时,可以动态的创建和销毁,如上面的举例的调色板的例子。
2、实现克隆操作,在.net中可以使用object类的MemberwiseClone()方法来实现对象的浅表复制或通过序列化的方法来实现深层的拷贝。
3、原型模式同样用于隔离类对象的使用者和具体类型(易变类)之间的耦合关系,他们同样要求这些“易变类”拥有稳定的接口。
效果:
1、它对客户影藏了具体的产品类,因此减少了客户知道的名字和数目。
2、原型模式允许客户只通过注册原型实例就可以将具体产品类并入到系统中,客户可以运行时刻建立和删除原型。
3、减少了子类的结构,原型模式是克隆一个原型而不是请求工厂方法创建一个,所以不需要一个与具体产品类平行的create类的层次。
4、原型模式具有给一个应用软件动态的加载新功能的能力,由于原型模式的独立性高,可以很容易动态加载新功能而不影响老系统。
5、产品类不需要非得有任何先确定的等级结构,因为原型模式适用于任何的等级结构
6、原型模式的最主要缺点就是每一个类必须配备一个克隆方法,而且这个克隆方法需要对类的功能进行通盘考虑,这对全新的类来说不是很难,但对已有的类进行改造时,不一定是件容易的事。
实用性
在下列情况下,应当使用原型模式:
1、当一个系统应用系统独立与他的产品创建,构成和表示时;
2、当要实例化的类是在运行时刻指定时,例如动态加载,
3、为了避免创建一个与产品类层次平行的工厂类层次时
4,当一个类的实例只能有不同状态组合中的一种时,建立相应数目的原型并克隆他们可能比每次用合适的状态手工实例该类更方便一些。
总结
原型模式同工厂模式,同样对客户隐藏了对象的创建工作,但是,与通过一个类新星实例化来构建对象不同的是,原型模式通过拷贝一个现有的对象生成新对象的,达到了“隔离类对象的使用者和具体类型(易变类)之间的耦合关系”的目的。