(第Ⅱ部分 创建型模式篇)第5章 原型模式(Proty

系统 1781 0

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


更多文章、技术交流、商务合作、联系博主

微信扫码或搜索:z360901061

微信扫一扫加我为好友

QQ号联系: 360901061

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描下面二维码支持博主2元、5元、10元、20元等您想捐的金额吧,狠狠点击下面给点支持吧,站长非常感激您!手机微信长按不能支付解决办法:请将微信支付二维码保存到相册,切换到微信,然后点击微信右上角扫一扫功能,选择支付二维码完成支付。

【本文对您有帮助就好】

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描上面二维码支持博主2元、5元、10元、自定义金额等您想捐的金额吧,站长会非常 感谢您的哦!!!

发表我的评论
最新评论 总共0条评论