.NET设计模式(6):原型模式(Prototype Pattern)

 

原型模式(Prototype Pattern

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

Terrylee20061

概述

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

意图

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

结构图

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

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

依赖倒置原则:上面的例子,原型管理器(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()方法来实现对象的浅表拷贝或通过序列化的方式来实现深拷贝。

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

效果

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

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

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

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

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

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

适用性

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

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

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

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

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

总结

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

参考文献

《设计模式》(中文版)

DesignPatternsExplained

Java与模式》(阎宏 著)

作者:TerryLee
出处:http://terrylee.cnblogs.com
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
posted @ 2006-01-16 09:13 TerryLee 阅读(37753) 评论(80) 编辑 收藏

 回复 引用   
#1楼 2006-01-16 10:14 void[未注册用户]
不错,看完后收益多多

 回复 引用 查看   
#2楼[楼主] 2006-01-16 16:58 Terrylee      
@void

设计模式的学习是一个长期的过程,需要不断地去思考,总结,实践……

 回复 引用 查看   
#3楼 2006-01-17 13:53 不会飞的鱼      
值得学习
 回复 引用   
#4楼 2006-02-07 17:34 shiner[未注册用户]
文章写得很好,易懂是最重要的,不错,让我明白了很多。我发现第4张图中的虚箭头好像反了,不知道对不对,如果不对那就见笑:),应该是对象工厂实例化对象才对!
 回复 引用 查看   
#5楼[楼主] 2006-02-16 14:45 Terrylee      
@shiner
多谢指出,确实存在错误!
已经更正,谢谢!!

 回复 引用   
#6楼 2006-02-27 09:47 mrhgw[未注册用户]
原型模式为什么要进行浅表或深层复制,为什么不直接引用原对象?
即:ConcteteColorPrototype c1 = (ConcteteColorPrototype)colormanager[colorName].Clone();
改成:
ConcteteColorPrototype c1 = (ConcteteColorPrototype)colormanager[colorName;
可不可以?大家帮我解解,谢了!

 回复 引用 查看   
#7楼[楼主] 2006-02-27 18:09 Terrylee      
@mrhgw

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

 回复 引用   
#8楼 2006-03-02 09:15 windaa[未注册用户]
为什么是:ConcteteColorPrototype c1 = (ConcteteColorPrototype)colormanager[colorName].Clone();

而不是ColorPrototype c1 = (ColorPrototype)colormanager[colorName].Clone();

 回复 引用   
#9楼 2006-03-06 23:07 rgx[未注册用户]
用调色板选择颜色作为prototype的例子,感觉不是很恰当。可能本人水平太差,理解不了。
colormanager["red"] = new ConcteteColorPrototype(255, 0, 0);
这句话注册一个原型,可以理解。
但是
ConcteteColorPrototype c1 = (ConcteteColorPrototype)colormanager[colorName].Clone();
我同楼上的兄弟一样的看法。之所以不行。是由于Prototype缺少一个virtual方法display。请指正

 回复 引用   
#10楼 2006-03-07 21:13 zhaojian[未注册用户]
写的好,例子通俗易懂,我同意楼上的看法
 回复 引用   
#11楼 2006-04-26 18:01 Mutou[未注册用户]
高手啊,不知我什么时候可以达到这种境界
 回复 引用   
#12楼 2006-08-07 18:13 kklc[未注册用户]
可能是我个人理解有问题,我觉得楼主的Color例子是完全错误的。完全体现不到原型的特点,而且你作为Client的App还是依赖于ConcteteColorPrototype,一点都没有改善耦合性,还增加了对ColorManager 的依赖。ColorManager 的存在该与原型模式没关系吧?一家之言,楼主莫怪。
 回复 引用 查看   
#13楼[楼主] 2006-08-08 08:16 TerryLee      
@kklc
依赖于ConcteteColorPrototype是可以使用其他的创建型模式(工厂方法,抽象工厂等)解决的,不要指望用一种模式可以解决所有的问题!

 回复 引用   
#14楼 2006-08-08 09:41 kklc[未注册用户]
对ConcteteColorPrototype的依赖只是表面的问题,主要的问题的整个结构都不符合,你对照上面的结构图,和例子完全不对应。说到最基本的,就是楼主认为在这个例子中原型起了什么作用,该例子又能应对哪方面的变化呢?原型的核心就在于把实例的创建控制在原型的创建,其他要使用的实例通过实例的Clone方法获得,而把原型的创建独立于客户代码,客户代码紧依赖于原型的接口,与原型的创建无关。这个是我的理解。有错漏的地方请指教,并包涵。
 回复 引用 查看   
#15楼[楼主] 2006-08-08 11:24 TerryLee      
@kklc
“……原型的创建独立于客户代码,客户代码紧依赖于原型的接口,与原型的创建无关。”,说的冠冕堂皇,其实还是没有说到Prototype模式的核心,为什么需要Prototype模式,那是因为我们面临的类是不确定的,动态的变化的,如果采用工厂模式,需要有与之对应的工厂类跟着变化。在Prototype模式中,我们封装的是动态变化!
对了还有一点可能没说明,这个例子来自于http://www.dofactory.com/

 回复 引用   
#16楼 2006-08-08 12:14 kklc[未注册用户]
@TerryLee
“其实还是没有说到Prototype模式的核心,为什么需要Prototype模式,那是因为我们面临的类是不确定的,动态的变化的,如果采用工厂模式,需要有与之对应的工厂类跟着变化。在Prototype模式中,我们封装的是动态变化!”
其实这个只是角度问题,你的观点的从使用原型的原因,而我是从实现这个目的的方法出发。如果你认为核心是该从原因出发的话,那我承认我错了就是了。
而从你的核心的观点出去,你觉得你的例子中封装了变化吗?只要具体类一变化客户代码立即要变化。你对照一下你的总结“Prototype模式同工厂模式,同样对客户隐藏了对象的创建工作,但是,与通过对一个类进行实例化来构造新对象不同的是,原型模式是通过拷贝一个现有对象生成新对象的,达到了“隔离类对象的使用者和具体类型(易变类)之间的耦合关系”的目的。”这个目的完全没有实现啊。
然后,我在http://www.dofactory.com/看不到这个例子。而且哪里来的例子也一样,我并不是对楼主能力的怀疑而觉得这个例子有问题,而是实在在这个例子看不到我理解中的原型。其实也不是看不到,我只是觉得App类不该这样使用原型实例,更不该把new写在App中。
我自问在模式方面造诣不深,只是就我自己的观点提出疑问,绝对没有找茬的意思。谢谢。

 回复 引用 查看   
#17楼[楼主] 2006-08-08 12:41 TerryLee      
@kklc
恕我直言,你已经钻进了模式的牛角,我上面说了不要依靠一种模式来解决所有问题,在App中出现New仅仅是是往ColorManager中添加颜色而已,在实际中这些决不会出现在App中,而且再说一下,这仅仅是一个示例而已!并不是一个完全可用的应用程序。多说无益。
http://www.dofactory.com/Patterns/PatternPrototype.aspx

 回复 引用 查看   
#18楼 2006-08-12 19:18 Ring      
TerryLee, 我不认为该模式满足了面向对象的设计的依赖倒置原则.

上文的ColorManager例子恰恰是由于你把该模式和Factory模式结合,由Factory模式替原型模式实现了依赖倒置.

我偏向于把原型模式理解成一种"拿来主义", 先前创建的对象直接就可以通过object.clone()拿过来用了。

 回复 引用 查看   
#19楼[楼主] 2006-08-14 08:37 TerryLee      
@Ring
对这个例子来说,确实是这样!

 回复 引用   
#20楼 2006-08-16 10:50 txd_lf[未注册用户]
对于你的例子,有太多的细节问题直接影响了你说明Prototype模式的效果~~
不要总是说“这仅仅是一个示例而已”~~而且我对于你学习的态度表示怀疑,因为一旦有人指出一些本质问题(不是书写错误),你总是试图去辩解~~既然这样就不要把它写出来

 回复 引用   
#21楼 2006-08-16 10:54 txd_lf[未注册用户]
还有不要删除别人的帖子~~如果这样我就对你的rp表示怀疑了~
 回复 引用 查看   
#22楼[楼主] 2006-08-16 11:23 TerryLee      
@txd_lf
说了半天,你好象也没有指出本质问题??

如果有你可以说出你的看法和见解,大家讨论一下,光在这儿骂人有什么用呢?

还有我对你的动机也产生了怀疑,你如果真的是来这儿讨论问题的话,为什么要匿名呢??

 回复 引用   
#23楼 2006-08-17 09:58 txd_lf[未注册用户]
1、首先原型实例的初始化不应该放在Main(客户端)里面,这不仅仅是一个示例,你要通过这个例子来传达这个模式的思想。
2、ConcteteColorPrototype c1 = (ConcteteColorPrototype)colormanager[colorName].Clone(false),真搞不明白怎么会有这样演示代码?

还有:
我什么时候骂你了?

我为什么不能匿名呢,我没有注册还不行吗

 回复 引用   
#24楼 2006-08-22 11:01 cosmic[未注册用户]
App只是为了让代码能够跑起来而已,ColorManager在哪里被实例化并不重要;重要的是Client如何获得属性定制的ConcreteColor的实例,即Clone在这里所起到的作用。

讨论中所说的类可能有点混乱。
看两种方式:
A、用new的方式(或者用工厂模式)来获得Color实例,则需要定义一堆RedColor,BlueColor类,而且这种做法还不能满足动态产生的新Color;
B、用Clone的方式就不必定义新的类了,可以直接从相应原型实例Clone。

在这里,ConcreteColor的不同原型实例实际上等同于不使用原型模式时需要定义的各种Color类。

所以,在这个例子里面,ConcreteColorPrototype是不会随着应用而变化的,变化的只是存在在ColorManager所持有的各种ConcreteColorPrototype实例。

另外,个人觉得在这里例子里面根本就没必要定义ColorPrototype,显然它引发了许多理解歧义。

 回复 引用   
#25楼 2006-08-26 13:41 trd[未注册用户]
楼主所表述的原型模式在概念上是没有错的,可能是在设计原型管理器的时候有点不太好。楼主的原型管理器是:
class ColorManager

{

Hashtable colors = new Hashtable();



public ColorPrototype this[string name]

{

get

{

return (ColorPrototype)colors[name];

}

set

{

colors.Add(name,value);

}

}

}

因为这里使用了属性的set来实现管理(这里只是添加)原型注册表(Hashtable colors ),但这样会出现一个问题,就是当客户端需要操作某种原型的时候(添加或删除)则必须事先知道这种原型所定义的具体的类,这就导致了在客户端暴露了某些本不需要客户端知道的信息。这个就好象一个“鸡生蛋,蛋生鸡”的问题。
我想可以这样改进一下:我们不使用属性来操作原型注册表,而是使用一个方法,把楼主原来客户端中的操作原型注册表的部分做适当的变换写入方法体中。
比如:
class ColorManager

{

Hashtable colors = new Hashtable();

public ColorPrototype this[string name]

{

get

{

return (ColorPrototype)colors[name];

}

/* set

{

colors.Add(name,value);

}去掉这个部分 */

}

public void AddColor(string name)
{

//根据颜色名创建相应的ConcteteColorPrototype对象并注册
If (name=="red"){
colors.Add(name,new ConcteteColorPrototype(255, 0, 0);}

... ...

}

}

而在客户端就相应的改为:
ColorManager colormanager = new ColorManager();

colormanager.AddColor("red");
... ...
其他的不变。

我也是一个新手,还希望请大家不吝赐教。

 回复 引用   
#26楼 2006-08-26 14:31 trd[未注册用户]
@cosmic

cosmic,其实是很有必要定义ColorPrototype的,仔细看看楼主属性的返回类型,就是ColorPrototype。
如果没有ColorPrototype,那么在有2个以上具体的原型类ConcteteColorPrototype1,ConcteteColorPrototype2的时候就会出现麻烦了。
这种技巧应该说很普通,在《C#高级编程》里面也讲的非常清楚。

 回复 引用   
#27楼 2006-08-26 14:37 trd[未注册用户]
@cosmic
App只是为了让代码能够跑起来而已,ColorManager在哪里被实例化并不重要;重要的是Client如何获得属性定制的ConcreteColor的实例,即Clone在这里所起到的作用。



我想你要说的是“ConcteteColorPrototype在哪里实例化并不重要”而不是“ColorManager在哪里被实例化并不重要”吧。

ColorManager就是一个原型管理器,用来在客户端管理原型的,不在客户端实例化还能在哪呢?

 回复 引用   
#28楼 2006-09-01 11:11 fuh[未注册用户]
能把模式理解得这么透不容易啊!!!!!
 回复 引用 查看   
#29楼 2006-09-07 17:08 努力学习的熊      
人太多了,我就不@了,麻烦!
我认为那个ColorManager使用的目的就是针对于颜色管理器来说的一个类而已,然后就像一位说的我们使用App为了把这个程序运转起来,在App中首先构造好了这个颜色管理器ColorManager(其间不考虑如何实现和是否合理,目的是需要一个颜色管理器,只要它能用我们就能继续下面的工作)。
在这里关于某位说在App中出现了new是十分不应该的,我就不说啥了,看到这里希望他能明白这个意思。而且后面的具体应用也没有出现这位大哥说的new啊。好像偶是没有看到的,呵呵:)而且确实是遵从原型模式Clone出来的对象啊。不过那位大哥要是有更好的实现模式可不可以在这里共享下哈,共同学习和进步嘛:)

对于这段代码:
string colorName = "red";
ConcteteColorPrototype c1 = (ConcteteColorPrototype)colormanager[colorName].Clone(false);
c1.Display(colorName);
我认为没有什么难理解的
string colorName = "red";
操作颜色管理器,告诉它我需要红色,就像鼠标点击的颜色管理器在其中进行选择时一样,这仅仅是用代码来表现一个场景。
ConcteteColorPrototype c1 = (ConcteteColorPrototype)colormanager[colorName].Clone(false);
这里完成的就是颜色管理器去完成管理工作,得知用户需要红色,那就返回给用户选择的红色。不带参的方法我认为没什么问题。如果仅仅就这个代码而言,使用了一个带参的Clone方法,我想这仅仅是告诉大家有时我们是需要进行深拷贝的,也许如上面某位说的实现的不是很好,但是我认为作者的意图不是为了写这个例子,并在这段代码上如何如何纠缠不清,而是告诉大家我们有时需要注意深拷贝的问题,对于值类型当然是浅拷贝就可以满足(也不会造成这种修改),引用类型也当然会用深拷贝了(这里可能确实修改的不太好,本人初学水平不高,期望那位仁兄能提供一个比较好的修改方式学习一下,感谢感谢)。
c1.Display(colorName);
将这个颜色返回,例如在画图时我们对画笔选择了红色,这时再画出的就是红色的线条。

对于App的编写不是说不重要,这里可能更关注的是模式,大家可以看看之前几篇对于使用反射来实现扩展,在App中可以构成一种相对不变稳定的架构。如果TerryLee或者哪位愿意完善这个小例子可以贴在这里,同志们继续讨论也未尝不可啊:)

 回复 引用 查看   
#30楼[楼主] 2006-09-07 17:19 TerryLee      
@努力学习的熊
谢谢回复这么多,呵呵

有时间我会完善一下这个例子:-)

 回复 引用   
#31楼 2006-11-01 17:47 tooqy[未注册用户]
请教一下:
原型模式和工厂方法模式,除了实现方式以及原型模式可以适应于动态变化外,还有什么区别。

按我上面这样理解去看的话,基本所有工厂方法模式都可以用原型模式来替代,而且实现起来更简单?
请请正,谢谢。

 回复 引用 查看   
#32楼[楼主] 2006-11-01 20:15 TerryLee      
@tooqy
简单的说吧,工厂模式在创建对象时还没有这个对象,由它创建的,而原型模式则是已经有了这个对象了,它快速复制出来了一个:)

 回复 引用 查看   
#33楼[楼主] 2006-11-01 20:16 TerryLee      
@tooqy
简单的说吧,工厂模式在创建对象时还没有这个对象,由它创建的,而原型模式则是已经有了这个对象了,它快速复制出来了一个:)

 回复 引用   
#34楼 2006-11-02 09:29 tooqy[未注册用户]
谢谢TerryLee的回复。也就是我理解的那样,他们最大区别就在于对象建站的方式?是吗。thank you.
 回复 引用 查看   
#35楼[楼主] 2006-11-02 19:31 TerryLee      
@tooqy
1.方式不同
2.是否已经有了对象

 回复 引用 查看   
#36楼 2006-11-23 12:50 立冬      
我认为有些时候问题越解释越复杂.

一句话,原型就是clone.至于你在什么地方,什么时间,什么场合使用clone那是你自己的事,与原型模式无关.你可能会找到无数种使用clone的理由.但记住这与原型无关.

 回复 引用 查看   
#37楼[楼主] 2006-11-23 20:55 TerryLee      
@立冬
使用本身就是一件很复杂的事情,比知道这个模式更复杂

 回复 引用   
#38楼 2006-11-23 22:22 立冬[匿名]
如果说使用的场合就多了去了,但使用它的目的是唯一的,即,我已经创建了这个对象,我不想再去重新创建,和平时大家用的复制 和粘贴道理一样,至于是用键盘来是鼠标还是用程序控件都没办法,和要复制什么也没关系。
 回复 引用   
#39楼 2006-12-11 15:59 balala[未注册用户]
我觉得上面为什么不使用Factory的例子有些欠妥,既然后面的例子可以把所有的颜色都抽象到一个类里,那么开始的Factory模式里也可以把所有的颜色都用一个Factory生成。

其实两者的唯一区别就是对象生成的时机,和方式。

 回复 引用 查看   
#40楼 2007-02-11 20:16 翁培铖      
看不懂,不过从评论中领悟到一些东西,设计模式这东西真不可钻牛角尖,感谢大佬 terry的一些想法。
 回复 引用 查看   
#41楼 2007-03-17 22:17 代码乱了      
string colorName = "red";
ConcteteColorPrototype c1 = (ConcteteColorPrototype)colormanager[colorName].Clone(false);
c1.Display(colorName);

和不用Clone的最后结果是一样的
ConcteteColorPrototype c1 = (ConcteteColorPrototype)colormanager[colorName];

搞不懂为什么这样?我认为最后colormanager中取得颜色的实例用不用Clone()方法都是一样的,应该用在加入colormanager到的时候
colormanager["flame"] = new ConcteteColorPrototype(211, 34, 20).Clone();




 回复 引用 查看   
#42楼 2007-06-06 14:31 何时若愚      
@ 代码乱了
那样管理器中就有许多冗余的对象了

 回复 引用   
#43楼 2007-08-29 16:01 kadaj[未注册用户]
感觉不对啊,ColorPrototype类只有一个Clone方法,为何不让ConcreteColor直接实现ICloneable接口呢?而App还是要知道ConcreteColor,是不是需要一个工厂啊?可不可以把ColorManager改成工厂?
我也是刚刚学设计模式,一头雾水啊

 回复 引用   
#44楼 2007-08-29 16:26 kadaj[未注册用户]
第二个问题好像明白过来了,实际只是“使用”一个对象,也就是可以在ColorManager里聚合一个:
ConcreteColor currentColor;
加个方法:
public ConcreteColor UseColor(string colorName)
{
currentColor = (ConcteteColor)colors[colorName].Clone();
currentColor.Display();
}
这样App只要知道ColorManager了。不知道这样对不对?还望赐教。

 回复 引用 查看   
#45楼 2007-09-27 00:21 郭磊      
主楼,如果只用浅副本的话,能不能把ColorManager写成这样啊:
class ColorManager
{

Hashtable colors = new Hashtable();

public ColorPrototype this[string name]
{

get
{

return ((ColorPrototype)colors[name]).Clone();

}

set
{

colors.Add(name, value);

}

}

这样在使用的时候就不会出现一堆的.Clone()方法了,不知道这样可以嘛?
我是初学者,学习.NET设计模式都是一直在看你的文章,觉得挺棒的.

 回复 引用 查看   
#46楼 2008-06-11 02:15 AimatMVP(初学者)      
@郭磊
这好像是个鸡生蛋蛋生鸡的问题。我是初学者,我想是这样的不知道对不对:
首先索引的Set方法是建立在Get方法之上的,只有Set没有Get没有任何意义。
按照上楼上那样写Set和Get的话
当我们初始化了ColorManage接着通过ColorManage实例初始化需要的原型时就出现问题了
colormanager["red"] = new ConcteteColorPrototype(255, 0, 0);
这里colormanager["red"]指向了((ColorPrototype)colors["red"]).Clone()而colors["red"]是在Set中初始化的即(ColorPrototype)colors[name]还没有初始化(这里鸡生蛋蛋生鸡)(ColorPrototype)colors[name]还没有初始化我们却试图调用它的Clone()方法,于是这就是问题了。
我的理解对吗?

 回复 引用 查看   
#47楼 2008-06-11 15:54 想你因为喝酒      
個人覺得用顏色來做例子有些不合適,而且lz用的manager導致很多新人會理解錯誤.我覺得標準的原型模式用於創建一個對象,並且該對象保存了原有對象的动态变化.
用遊戲中的角色來說會更好,比如某英雄a有一種魔法能用將自已分身出成兩個英雄b和c,同時b和c這兩個英雄又應該有a分身前的屬性.比如a分身前只有50%的血,b和c也只能有50%的血.
如果用其它模式恐怕無法創建這樣的對象,因為重新創建后對象的屬性將會重新初始化.

 回复 引用 查看   
#48楼 2008-07-10 12:05 gotolovo      
--引用-------------------------------------------------- 水言木: 不知道是不是太困了看傻了,这次(第二次看到这篇文章)看时竟然有点不理解了:
EventLog eventlog = new EventLog();
eventlog.Write();
这里通过基类来调用:
Log log = new EventLog();
log.Write();
这样的话和:
LogFactory factory = new EventFactory();
Log log = factory.Create();
log.Write();
相比会差很多?感觉其实是一样的啊
请指点... --------------------------------------------------------
困惑一段时间了 还请指教啊

 回复 引用   
#49楼 2008-11-03 10:59 王文斌[未注册用户]
其实我有个想法哦,原型模式使用的时候,其实必须要事先初始化所有原型,然后在需要的时候进行Clone的对吧,在Manager里面不能通过反射的方式,读取配置文件,在初始化的时候(设置static {}感觉也可以)读取配置文件,自动初始化所有原型(颜色) 配置文件可以(名称,颜色值)这样定义,读取的时候,读到相应项目初始话color并加入hashtable.这样不就封装了动态变化了吗。客户端调用的时候,Manager已经初始化了所有原型,直接获取即可,就不存在大家的问题了吧。个人浅见。望楼主指教。
 回复 引用   
#50楼 2008-11-03 11:21 王文斌[未注册用户]
简单改了一下,比较懒,直接用csv文件,没用xml
首先Manager构造方法
public ColorManager()
{

StreamReader fs = new StreamReader(File.OpenRead("ColorConfig.txt"));
string str = "";
char[] sp = {','};
while (true)
{
str = fs.ReadLine();
if (string.IsNullOrEmpty(str))
break;
string[] colorinfo = str.Split(sp);

colors[colorinfo[0]] = new ConcteteColorPrototype(
int.Parse(colorinfo[1]),
int.Parse(colorinfo[2]),
int.Parse(colorinfo[3])
);

}

}
然后csv文件
red,255,0,0
green,0,255,0
blue,0,0,255
angry,255,54,0
peace,128,211,128
flame,211,34,20


 回复 引用 查看   
#51楼 2008-12-14 17:50 pillow      
我觉得prototype主要是针对资源优化来的。因为new出来的对象消耗资源。
第二,就是减少耦合。我认为这段示例只体现了第二点。没有体现第一点。我认为客户端的代码创建了一个原型(red)之后不应该再用new了。而应该依据原型对象修改参数得到另一个复制品(green)。这样里面的clone才有作用。否则本来就有对象了,还去clone一个。没有起到作用。

 回复 引用 查看   
#52楼[楼主] 2008-12-15 11:26 TerryLee      
@pillow
关于prototype模式的认识我同意这一点,它是一个非常细粒度的模式;但是并不是修改原有对象的参数就能得到一个复制品,要得到原有对象的复制品,必须clone一个,不是仅仅修改参数。

 回复 引用 查看   
#53楼 2008-12-15 11:54 pillow      
我的意思是通过原型对象里面的Clone复制一个或多个相同的对象,而不用new了。用new创建一个red原型对象,用原型对象里面的clone方法复制一个或多个复制品。如果复制品要求不同。原型对象里面可以提供属性,以便适应不同的需求。还望指点!
 回复 引用 查看   
#54楼 2008-12-25 23:13 pillow      
当我再看博主的protype时,又有了新的收获。原来是我错了。
 回复 引用 查看   
#55楼 2009-01-04 10:45 Fengdesudu      
很好
 回复 引用 查看   
#56楼[楼主] 2009-01-04 11:17 TerryLee      
@pillow
:)

 回复 引用 查看   
#57楼[楼主] 2009-01-04 11:17 TerryLee      
@Fengdesudu
谢谢:)

 回复 引用 查看   
#58楼 2009-01-04 13:25 会长      
如果楼主要出书,是否可以摘录一些文章的读者与楼主之间的问答上去。。。呵呵
 回复 引用 查看   
#59楼 2009-01-04 13:25 会长      
最好把我这几句放上去。。呵呵
 回复 引用 查看   
#60楼[楼主] 2009-01-05 01:22 TerryLee      
@会长
目前不会考虑出设计模式的书,如果出,一定放上去,呵呵

 回复 引用 查看   
#61楼 2009-01-05 12:38 会长      
@TerryLee
谢谢!!:-)

 回复 引用 查看   
#62楼 2009-01-07 12:54 pillow      
过年了。工作也该停停了。抽点时间将所以文章写完。造福我们!!
 回复 引用 查看   
#63楼 2009-01-07 13:24 pillow      
我理解的protype 有两点第一点就是封装了等级结构的动态变化。这点是和工厂模式的本质区别。工厂模式可以封装new.但是应对这种动态等级变化的能力很弱。特别是这种等级变化很多的时候。不知道要有多少工厂类。protype是以牺牲new换取这种等级的动态变化的灵活性;第二点是clone,应该需要由new来应对这种等级变化。在使用这种等级变化的时候,就可以clone了,避免太多的new。我认为Protype的核心还是第一点。不知道对么?
 回复 引用 查看   
#64楼[楼主] 2009-01-12 11:36 TerryLee      
@pillow
为啥过年了工作就可以停呢?年底的时候是一年中最忙的时候!

 回复 引用 查看   
#65楼 2009-03-13 18:31 停留的风      
lz分析的很好。认真阅读之后,并未感受到原型模式的强大之处。
前辈的例子,完全可以在ConcteteColorPrototype加一个类别标识。
public ConcteteColorPrototype(int red, int green, int blue, string name)

{

this._red = red;

this._green = green;

this._blue = blue;

this._name=name;

}
然后,管理类和you设计的差不多,我只要在实例化的时候,指定其color的类别即可。
好像比你这样实现,更加简单易读。为什么还要绕一大圈,还要clone?
希望lz给一个更好的解释。

 回复 引用 查看   
#66楼 2009-06-11 16:35 昨夜飘风      
clone 肤浅复制的确和直接指定没什么区别,但是深度复制后,区别就比较明显了,深度复制后,虽然不NEW出来的,却具备教好的独立性。

下面是两种复制的区别
对象的复制,区别在于复制的类别不同:深复制会复制整个填充的对象,包括该对象中其他引用类型和值类型的值;而浅复制只复制了一个对象中所有引用,它没有值的复制,通过引用它们的其他对象的引用来共享它们。

 回复 引用 查看   
#67楼 2009-06-11 16:37 昨夜飘风      
所以当客户不希望类之间发生冲突的时候,就可以采取深度复制,或者你NEW就NEW吧
 回复 引用 查看   
#68楼 2009-06-21 10:54 mrxliu      
@想你因为喝酒

你的那个例子很通俗也。呵呵,就像魔兽里面的剑圣,分身之后,拥有本体的一半属性,用 原型模式解决,哈哈。

 回复 引用 查看   
#69楼 2009-11-25 20:57 my36z      
引用想你因为喝酒:個人覺得用顏色來做例子有些不合適,而且lz用的manager導致很多新人會理解錯誤.我覺得標準的原型模式用於創建一個對象,並且該對象保存了原有對象的动态变化.<br>用遊戲中的角色來說會更好,比如某英雄a有一種魔法能用將自已分身出成兩個英雄b和c,同時b和c這兩個英雄又應該有a分身前的屬性.比如a分身前只有50%的血,b和c也只能有50%的血.<br>如果用其它模式恐怕無法創建這樣的對象,因為重新創建后對象的屬性將會重新初始化.

这句一看就懂。但是不知道是不是这样啊。按效果来讲,原型不用new的好处就是克隆一个实例,这个实例不是全新的,不是初始状态的。

 回复 引用   
#70楼 2009-12-21 23:57 danny99899[未注册用户]
晕~看了那么多留言,你们好象都很困惑,不知道大家有没有熟悉javascript里的prototype属性
这个书prototype 跟prototype 模式差不多的

在C#里我不知道prototype 有没有用,因为prototype 模式我也认同楼主的,动态创建新的类型(基于原由的类型),但是在C#里,3.0之前还不是动态语言..
直接动态扩展了某一个类型例如
原型:
class Person{
public string name{}
}
扩展Person 加一个age 属性
那C#应该里应该是可以 这样引用的,NewPerson.Age

但是由于编译器的问题不可以,除非你反射调用咯

但是在javascript 里可以这样写
function Person(name)
{
this.name =name;
}
var p = new Person("danny99899");
p.prototype.age = 23;
当然这里还可以这样写,p.age = 23;效果跟prototype 引用是一样的

这样就可以直接引用p.age
而且在ecma 标准里,根据prototype属性可以直接调用对象.call(对象原形)来扩展当前调用call 的对象..
我想这就是原型的特点...

不知道说得对不对,请指点...

 回复 引用   
#71楼 2009-12-22 00:00 danny99899[未注册用户]
最不明白的就是,在C#这个非动态语言里,怎么样动态构造新的类型呢...
 回复 引用   
#72楼 2009-12-22 00:01 danny99899[未注册用户]
如果说要用CodeDom的话,那效率不是很低吗
 回复 引用 查看   
#73楼 2010-01-25 17:11 Nero.Pang      
我看了一遍,ConcteteColorPrototype类里的Clone有什么作用呢?例子里似乎没有体现出来!
 回复 引用 查看   
#74楼 2010-06-02 16:49 自己就是宝藏      
学习了。
 回复 引用 查看   
#75楼 2010-06-23 22:25 刘标才      
用了DataTable的Clone方法就知道了,为什么DataTable要这个方法,原因是当得到一个DataTable对象的时候,又想创建一个一样的
DataTable的话,如果没有Clone,那么就要创建Columns,这个是很麻烦的,但是有了Clone就很爽了,直接调用就行

 回复 引用 查看   
#76楼 2010-09-19 15:45 风从指尖飘过      
才开始学习, 有源代码嘛,我不喜欢看例子,养成坏习惯了, 我喜欢看源码。
 回复 引用 查看   
#77楼 2011-07-20 22:16 yanghe1117      
引用努力学习的熊:人太多了,我就不@了,麻烦!
<br>我认为那个ColorManager使用的目的就是针对于颜色管理器来说的一个类而已,然后就像一位说的我们使用App为了把这个程序运转起来,在App中首先构造好了这个颜色管理器ColorManager(其间不考虑如何实现和是否合理,目的是需要一个颜色管理器,只要它能用我们就能继续下面的工作)。
<br>在这里关于某位说在App中出现了new是十分不应该的,我就不说啥了,看到这里希望他能明白这个意思。而且后面的具体应用也没有出现这位大哥说的new啊。好像偶是没有看到的,呵呵:)而且确实是遵从原型模式Clone出来的对象啊。不过那位大哥要是有更好的实...

顶个

 回复 引用 查看   
#78楼 2012-01-15 11:04 realylz      
朦胧中从Lee获得了智慧的力量,但总是感觉劲不打一处使。模式之美,爱恨交加。