NET设计模式 第二部分 创建型模式(5):原型模式(Prototype Pattern)

原型模式(Prototype Pattern)

——.NET设计模式系列之六

Terrylee,2006年1月

概述

在软件系统中,有时候面临的产品类是动态变化的,而且这个产品类具有一定的等级结构。这时如果用工厂模式,则与产品类等级结构平行的工厂方法类也要随着这种变化而变化,显然不大合适。那么如何封装这种动态的变化?从而使依赖于这些易变对象的客户程序不随着产品类变化?

意图

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

结构图

Prototype模式结构图

生活中的例子

Prototype模式使用原型实例指定创建对象的种类。新产品的原型通常是先于全部产品建立的,这样的原型是被动的,并不参与复制它自己。一个细胞的有丝分裂,产生两个同样的细胞,是一个扮演主动角色复制自己原型的例子,这演示了原型模式。一个细胞分裂,产生两个同样基因型的细胞。换句话说,细胞克隆了自己。

使用细胞分裂例子的Prototype模式对象图

原型模式解说

我们考虑这样一个场景,假定我们要开发一个调色板,用户单击调色板上任一个方块,将会返回一个对应的颜色的实例,下面我们看看如何通过原型模式来达到系统动态加载具体产品的目的。

很自然,我们利用OO的思想,把每一种颜色作为一个对象,并为他们抽象出一个公用的父类,如下图:

实现代码:

public abstract class Color

{

public abstract void Display();

}

public class RedColor:Color

{

public override void Display()

{

Console.WriteLine("Red‘s RGB Values are:255,0,0");

}

}

public class GreenColor:Color

{

public override void Display()

{

Console.WriteLine("Green‘s RGB Values are:0,255,0");

}

}

客户程序需要某一种颜色的时候,只需要创建对应的具体类的实例就可以了。但是这样我们并没有达到封装变化点的目的,也许你会说,可以使用工厂方法模式,为每一个具体子类定义一个与其等级平行的工厂类,那么好,看一下实现:

实现代码:

public abstract class ColorFactory

{

public abstract Color Create();

}

public class RedFactory:ColorFactory

{

public override Color Create()

{

return new RedColor();

}

}

public class GreenFactory:ColorFactory

{

public override Color Create()

{

return new GreenColor();

}

}

实现了这一步之后,可以看到,客户程序只要调用工厂方法就可以了。似乎我们用工厂方法模式来解决是没有问题的。但是,我们考虑的仅仅是封装了new变化,而没有考虑颜色的数量是不断变化的,甚至可能是在程序运行的过程中动态增加和减少的,那么用这种方法实现,随着颜色数量的不断增加,子类的数量会迅速膨大,导致子类过多,显然用工厂方法模式有些不大合适。

进一步思考,这些Color子类仅仅在初始化的颜色对象类别上有所不同。添加一个ColorTool这样的类,来参数化的它的实例,而这些实例是由Color支持和创建的。我们让ColorTool通过克隆或者拷贝一个Color子类的实例来创建新的Color,这个实例就是一个原型。如下图所示:

实现代码:

abstract class ColorPrototype

{

public abstract ColorPrototype Clone();

}

class ConcteteColorPrototype : ColorPrototype

{

private int _red, _green, _blue;

public ConcteteColorPrototype(int red, int green, int blue)

{

this._red = red;

this._green = green;

this._blue = blue;

}

public override ColorPrototype Clone()

{

//实现浅拷贝

return (ColorPrototype) this.MemberwiseClone();

}

public void Display(string _colorname)

{

Console.WriteLine("{0}‘s RGB Values are: {1},{2},{3}",

_colorname,_red, _green, _blue );

}

}

class ColorManager

{

Hashtable colors = new Hashtable();

public ColorPrototype this[string name]

{

get

{

return (ColorPrototype)colors[name];

}

set

{

colors.Add(name,value);

}

}

}

现在我们分析一下,这样带来了什么好处?首先从子类的数目上大大减少了,不需要再为每一种具体的颜色产品而定一个类和与它等级平行的工厂方法类,而ColorTool则扮演了原型管理器的角色。再看一下为客户程序的实现:

class App

{

public static void Main(string[] args)

{

ColorManager colormanager = new ColorManager();

//初始化颜色

colormanager["red"] = new ConcteteColorPrototype(255, 0, 0);

colormanager["green"] = new ConcteteColorPrototype(0, 255, 0);

colormanager["blue"] = new ConcteteColorPrototype(0, 0, 255);

colormanager["angry"] = new ConcteteColorPrototype(255, 54, 0);

colormanager["peace"] = new ConcteteColorPrototype(128, 211, 128);

colormanager["flame"] = new ConcteteColorPrototype(211, 34, 20);

//使用颜色

string colorName = "red";

ConcteteColorPrototype c1 = (ConcteteColorPrototype)colormanager[colorName].Clone();

c1.Display(colorName);

colorName = "peace";

ConcteteColorPrototype c2 = (ConcteteColorPrototype)colormanager[colorName].Clone();

c2.Display(colorName);

colorName = "flame";

ConcteteColorPrototype c3 = (ConcteteColorPrototype)colormanager[colorName].Clone();

c3.Display(colorName);

Console.ReadLine();

}

}

可以看到,客户程序通过注册原型实例就可以将一个具体产品类并入到系统中,在运行时刻,可以动态的建立和删除原型。最后还要注意一点,在上面的例子中,用的是浅表复制。如果想做深复制,需要通过序列化的方式来实现。经过了上面的分析之后,我们再来思考下面的问题:

1.为什么需要Prototype模式?

引入原型模式的本质在于利用已有的一个原型对象,快速的生成和原型对象一样的实例。你有一个A的实例a:A a = new A();现在你想生成和car1一样的一个实例b,按照原型模式,应该是这样:A b = a.Clone();而不是重新再new一个A对象。通过上面这句话就可以得到一个和a一样的实例,确切的说,应该是它们的数据成员是一样的。Prototype模式同样是返回了一个A对象而没有使用new操作。

2.引入Prototype模式带来了什么好处?

可以看到,引入Prototype模式后我们不再需要一个与具体产品等级结构平行的工厂方法类,减少了类的构造,同时客户程序可以在运行时刻建立和删除原型。

3.Prototype模式满足了哪些面向对象的设计原则?

依赖倒置原则:上面的例子,原型管理器(ColorManager)仅仅依赖于抽象部分(ColorPrototype),而具体实现细节(ConcteteColorPrototype)则依赖与抽象部分(ColorPrototype),所以Prototype很好的满足了依赖倒置原则。

通过序列化实现深拷贝

要实现深拷贝,可以通过序列化的方式。抽象类及具体类都必须标注为可序列化的[Serializable],上面的例子加上深拷贝之后的完整程序如下:

using System;

using System.Collections;

using System.IO;

using System.Runtime.Serialization;

using System.Runtime.Serialization.Formatters.Binary;

[Serializable]

abstract class ColorPrototype

{

public abstract ColorPrototype Clone(bool Deep);

}

[Serializable]

class ConcteteColorPrototype : ColorPrototype

{

private int _red, _green, _blue;

public ConcteteColorPrototype(int red, int green, int blue)

{

this._red = red;

this._green = green;

this._blue = blue;

}

public override ColorPrototype Clone(bool Deep)

{

if(Deep)

return CreateDeepCopy();

else

return (ColorPrototype) this.MemberwiseClone();

}

//实现深拷贝

public ColorPrototype CreateDeepCopy()

{

ColorPrototype colorPrototype;

MemoryStream memoryStream = new MemoryStream();

BinaryFormatter formatter = new BinaryFormatter();

formatter.Serialize(memoryStream, this);

memoryStream.Position = 0;

colorPrototype = (ColorPrototype)formatter.Deserialize(memoryStream);

return colorPrototype;

}

public ConcteteColorPrototype Create(int red,int green,int blue)

{

return new ConcteteColorPrototype(red,green,blue);

}

public void Display(string _colorname)

{

Console.WriteLine("{0}‘s RGB Values are: {1},{2},{3}",

_colorname,_red, _green, _blue );

}

}

class ColorManager

{

Hashtable colors = new Hashtable();

public ColorPrototype this[string name]

{

get

{

return (ColorPrototype)colors[name];

}

set

{

colors.Add(name,value);

}

}

}

class App

{

public static void Main(string[] args)

{

ColorManager colormanager = new ColorManager();

//初始化颜色

colormanager["red"] = new ConcteteColorPrototype(255, 0, 0);

colormanager["green"] = new ConcteteColorPrototype(0, 255, 0);

colormanager["blue"] = new ConcteteColorPrototype(0, 0, 255);

colormanager["angry"] = new ConcteteColorPrototype(255, 54, 0);

colormanager["peace"] = new ConcteteColorPrototype(128, 211, 128);

colormanager["flame"] = new ConcteteColorPrototype(211, 34, 20);

//使用颜色

string colorName = "red";

ConcteteColorPrototype c1 = (ConcteteColorPrototype)colormanager[colorName].Clone(false);

c1.Display(colorName);

colorName = "peace";

ConcteteColorPrototype c2 = (ConcteteColorPrototype)colormanager[colorName].Clone(true);

c2.Display(colorName);

colorName = "flame";

ConcteteColorPrototype c3 = (ConcteteColorPrototype)colormanager[colorName].Clone(true);

c3.Display(colorName);

Console.ReadLine();

}

}

实现要点

1.使用原型管理器,体现在一个系统中原型数目不固定时,可以动态的创建和销毁,如上面的举的调色板的例子。

2.实现克隆操作,在.NET中可以使用Object类的MemberwiseClone()方法来实现对象的浅表拷贝或通过序列化的方式来实现深拷贝。

3.Prototype模式同样用于隔离类对象的使用者和具体类型(易变类)之间的耦合关系,它同样要求这些“易变类”拥有稳定的接口。

效果

1.它对客户隐藏了具体的产品类,因此减少了客户知道的名字的数目。

2.Prototype模式允许客户只通过注册原型实例就可以将一个具体产品类并入到系统中,客户可以在运行时刻建立和删除原型。

3.减少了子类构造,Prototype模式是克隆一个原型而不是请求工厂方法创建一个,所以它不需要一个与具体产品类平行的Creater类层次。

4.Portotype模式具有给一个应用软件动态加载新功能的能力。由于Prototype的独立性较高,可以很容易动态加载新功能而不影响老系统。

5.产品类不需要非得有任何事先确定的等级结构,因为Prototype模式适用于任何的等级结构

6.Prototype模式的最主要缺点就是每一个类必须配备一个克隆方法。而且这个克隆方法需要对类的功能进行通盘考虑,这对全新的类来说不是很难,但对已有的类进行改造时,不一定是件容易的事。

适用性

在下列情况下,应当使用Prototype模式:

1.当一个系统应该独立于它的产品创建,构成和表示时;

2.当要实例化的类是在运行时刻指定时,例如,通过动态装载;

3.为了避免创建一个与产品类层次平行的工厂类层次时;

4.当一个类的实例只能有几个不同状态组合中的一种时。建立相应数目的原型并克隆它们可能比每次用合适的状态手工实例化该类更方便一些。

总结

Prototype模式同工厂模式,同样对客户隐藏了对象的创建工作,但是,与通过对一个类进行实例化来构造新对象不同的是,原型模式是通过拷贝一个现有对象生成新对象的,达到了“隔离类对象的使用者和具体类型(易变类)之间的耦合关系”的目的。

参考文献

《设计模式》(中文版)

《DesignPatternsExplained》

《Java与模式》(阎宏 著)

时间: 2024-11-25 07:53:56

NET设计模式 第二部分 创建型模式(5):原型模式(Prototype Pattern)的相关文章

NET设计模式 第二部分 创建型模式(6):创建型模式专题总结(Creational Pattern)

创建型模式专题总结(Creational Pattern) ——.NET设计模式系列之七 Terrylee,2006年1月 概述 创建型模式,就是用来创建对象的模式,抽象了实例化的过程.它帮助一个系统独立于如何创建.组合和表示它的那些对象.本文对五种常用创建型模式进行了比较,通过一个游戏开发场景的例子来说该如何使用创建型模式. 为什么需要创建型模式 所有的创建型模式都有两个永恒的主旋律:第一,它们都将系统使用哪些具体类的信息封装起来:第二,它们隐藏了这些类的实例是如何被创建和组织的.外界对于这些

.NET设计模式 第二部分 创建型模式(3)—建造者模式(Builder Pattern)

建造者模式(Builder Pattern) ——.NET设计模式系列之四 Terrylee,2005年12月17日 概述 在软件系统中,有时候面临着“一个复杂对象”的创建工作,其通常由各个部分的子对象用一定的算法构成:由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们组合在一起的算法确相对稳定.如何应对这种变化?如何提供一种“封装机制”来隔离出“复杂对象的各个部分”的变化,从而保持系统中的“稳定构建算法”不随着需求改变而改变?这就是要说的建造者模式. 本文通过现实生活中的买

NET设计模式 第二部分 创建型模式(4):工厂方法模式(Factory Method)

工厂方法模式(Factory Method) ——.NET设计模式系列之五 Terrylee,2004年1月2日 概述 在软件系统中,经常面临着“某个对象”的创建工作,由于需求的变化,这个对象的具体实现经常面临着剧烈的变化,但是它却拥有比较稳定的接口.如何应对这种变化?提供一种封装机制来隔离出“这个易变对象”的变化,从而保持系统中“其它依赖该对象的对象”不随着需求的改变而改变?这就是要说的Factory Method模式了. 意图 定义一个用户创建对象的接口,让子类决定实例化哪一个类.Facto

设计模式(3)-对象创建型模式-Abstract Factory模式

1.对象创建型模式 1.3           Abstract Factory模式 1.3.1 需求 在下面情况能够使用Abstract Factory模式: ?  一个系统要独立于它的产品的创建.组合和表示时(这个需求和FactoryMethod类似). ?  一个系统要由多个产品系列中的一个来配置时(这个需求也和Factory Method类似). ?  当你要强调一系列相关的产品对象的设计以便进行联合使用时(这个需求表明一个工厂要创建多个相关的产品对象,是比FactoryMethod多的

天天设计模式二:创建型模式实践

天天设计模式二:创建型模式实践 创建型设计模式主要应用在对象创建,以不同的方式来满足系统的灵活性配置.动态注入.等应用场景. 一.单例模式 二.抽象工厂 三.建造者模式 四.工厂方法模式 五.原型模式

Java设计模式之五大创建型模式(附实例和详解)

一.概况 总体来说设计模式分为三大类: (1)创建型模式,共五种:工厂方法模式.抽象工厂模式.单例模式.建造者模式.原型模式. (2)结构型模式,共七种:适配器模式.装饰器模式.代理模式.外观模式.桥接模式.组合模式.享元模式. (3)行为型模式,共十一种:策略模式.模板方法模式.观察者模式.迭代子模式.责任链模式.命令模式.备忘录模式.状态模式.访问者模式.中介者模式.解释器模式. 二.设计模式的六大原则 1.开闭原则(Open Close Principle) 开闭原则就是说对扩展开放,对修

设计模式一:创建型模式

知识储备: 1.接口:若干抽象方法的集合 作用:限制实现接口的类必须按照接口给定的调用方式实现这些方法:对高层模块隐藏了类的内部实现. 2.面向对象设计SOLID原则 开放封闭原则:一个软件实体如类.模块和函数应该对扩展开放,对修改关闭.即软件实体应尽量在不修改原有代码的情况下进行扩展. 里氏替换原则:所有引用父类的地方必须能透明的使用其子类的对象. 依赖倒置原则:高层模块不应该依赖底层模块,二者都应该依赖其抽象:抽象不应该依赖细节:细节应该依赖抽象.换言之,要针对接口编程,而不是针对实现编程.

小菜学设计模式——设计模式总结之创建型

1.面向过程与面向对象 1)面向过程通过划分功能模块,通过函数间相互调用来实现,但需求变化时就需要更改函数,而你改动的函数有多少地方在调用她呢?关联多少数据,这是很不容易弄得清楚地地方.或许开发者本人弄得清楚,但是下一个维护人员未必能够吃透.所以,由于这种强耦合性直接导致程序的扩展性和可维护性降低,后期维护的成本自然增高了不少. 2)面向对象关注的是对象,对象的优点在于,可以定义自己负责的事务,做要求它自己做的事情.对象应该自己负责自己,而且应该清楚的定义责任. 3)一般面向对象开发只是关心这么

创建型模式:原型模式

个人公众号原文: 创建型模式:原型模式 五大创建型模式之五:原型模式. 简介 姓名 :原型模式 英文名 :Prototype Pattern 价值观 :效率第一 个人介绍 : Specify the kinds of objects to create using a prototypical instance,and create new objects by copying this prototype. 用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象. (来自<设计模式之禅