第十二课 原型模式

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

      上面就是原型模式的定义了。那么为什么我们要采用原型模式呢?设想一下,现在有这么一个东西,其实可以把它抽象出一个类,但是由于他属性有着千变万化,而每种变化可能都是我们所需要的。那么解决方案呢?我想大家能马上想到以下方式:

1.    在使用的时候初始化不同属性。

2.    使用类似工厂模式的方法,把每个变化作为一个产品。通过工厂生产产品。

3.    使用建造者模式,建造不同属性的对象出来。


首先看 1 这种方法(估计大家都这么办的,别砸我哦),他直接在使用的时候进行初始化,我说这种方式不可取,每次都要初始化不同信息,耦合性高不说,垃圾代码也多,不符合咱们设计模式的原则。

然后看 2 这种方法(在了解原型前,大家最容易想到的方法,其实能想到已经不错了)。工厂方法确实可以解决问题,但是,由于变化过多,可能导致工厂类过多,并不适合在这里使用。

再看 3 这种方法。刚讲完了建造者方法,想用也是正常的。这里用来初始化属性看着也没错,但是同样的,需要多少建造方法呀。变化是动态产生的,每种变化都要一个吗?


这里就引出原型模式了。像这种产品类是动态变化的,但是其实可以进一步抽象出来相同点,转变为同一对象不同属性这种方式(个人理解,有待商榷)。这时就可以考虑使用原型模式。

下面引用一个例子。(在李会军博客看到的,我稍微进行了下改进。主要是使用了配置的方式初始化管理对象)


      下面采用示例来说明。现在要实现一个假调色板,上面有个种颜色可以选定。一般看到这里都会想到,把Color这个对象抽象出来吧。就像下面代码:

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();

   }

}


确实好像工厂方法能够完美的封装变化进去。但是我如果想要添加yellow呢?blue呢?你说了,我只要实现相应的产品类,然后实现相应的工厂方法。。。。。。发现问题了吧,颜色那么多,需要多少子类啊!!!

这里再仔细想想我上面说的,能够抽象出一个对象Color,不同颜色也只是具体属性不同是吧?其实就是初始化的值不同呗?那么不正好用原型模式吗。(个人理解)

下面就是原型方式的实现:

using System;

using System.Collections;

using System.Collections.Generic;

using System.Text;

using System.IO;

namespace 原型模式

{

    public class ColorSample

    {

        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();


        }



    }


    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 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])

                    );


            }


        }



        public ColorPrototype this[string name]

        {


            get

            {


                return (ColorPrototype)colors[name];


            }


            set

            {


                colors.Add(name, value);


            }


        }


    }


}

这里我使用了读取配置文件的方式初始化原型管理类,配置文件如下

red,255,0,0

green,0,255,0

blue,0,0,255

angry,255,54,0

peace,128,211,128

flame,211,34,20

比较懒,没使用XML,直接用的csv文件。

下面切入正题。(想尝试先看代码后分析的方法)

原型模式的实现方法如下图所示。



第十二课 原型模式_设计模式所谓原型,就是实现将多个对象初始化。然后在需要对象的时候,通过Clone()方法(喜欢交Copy()也随你),获得已经初始化好的对象的副本。从而实现快速获得对象的方式。在实际应用中,多数采取通过一个Manager对象的方法来管理原型对象。如下图。



第十二课 原型模式_colors_02ColorTool就是一个Manager(管理)类,用来管理所有的原型对象。

好了,现在大概也了解了原型模式的实现方法了。来套示例代码吧。

首先,ColorPrototype 就是原型对象了,其实一般应用时多直接使用 ICloneable (.net) , Cloneable (Java)直接作为Clone()接口哦。

然后ConcteteColorPrototype 就是真正的抽象出来的对象了。这里就是颜色对象。里面定义了RGB的3个参数,其实参数不同,就是不同颜色(表告诉我不知道RGB)。

再来看管理类ColorManager ,其实就是图中的ColorTool,它里面定义了一个HashTable,用来存放已经设定的原型对象。使用索引属性的方式来设定和取得原型对象。

这里大家抛去构造函数外,就是李会军博客里的示例。主函数里面注释掉的,就是原始用法。围绕着这里进行过深入讨论

我这里做了下处理,通过配置文件的方式初始化了一下所有的原型对象。这样规避了里面提到的一些问题。其实正如李会军博客里面说的,不要指望一个设计模式解决所有问题。模式是为了解决问题,只要能方便用,怎么处理不好。

呵呵,废话完了,坐下总结。


原型模式,说白了就是先创建一个对象,把他初始化,然后再需要同样对象的时候,直接通过.clone()方法,获得这个对象的副本。从而省去了初始化等等操作。方便获得现成对象。


补充一下。示例ConcteteColorPrototype中的Clone()方法是用的浅表复制。相对应的,还有深拷贝方法。一般是通过序列化对象方式进行复制。区别是什么呢?一般类中很可能定义有引用类型的属性对吧,浅拷贝中,相同引用属性,指向的是相同对象,也就是说,修改浅拷贝对象的引用属性,会影响到原型的引用属性。而深拷贝不同,他会完全副本所有属性,可以放心使用。但是相对的,操作消耗的资源也大,使用的时候需要衡量一下。下面给出支持2中覆盖的源码。

using System;

using System.Collections;

using System.Collections.Generic;

using System.Text;

using System.IO;

using System.Runtime.Serialization;


using System.Runtime.Serialization.Formatters.Binary;





namespace 原型模式

{

    public class ColorSample

    {

        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(true);


            c1.Display(colorName);



            colorName = "peace";


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


            c2.Display(colorName);



            colorName = "flame";


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


            c3.Display(colorName);



            Console.ReadLine();


        }



    }

    [Serializable]

    abstract class ColorPrototype

    {


        public abstract ColorPrototype Clone(bool deep);


    }


    [Serializable]

    class ConcteteColorPrototype : ColorPrototype

    {



        private int _red, _green, _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 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 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])

                    );


            }


        }



        public ColorPrototype this[string name]

        {


            get

            {


                return (ColorPrototype)colors[name];


            }


            set

            {


                colors.Add(name, value);


            }


        }


    }


}

好了,这一课就到这里吧。对了,12月初有个日语二级考试,最近可能需要复习一下,而且最近领导抓一些事情,可能更新速度会有所降低。(其实主要我也要现学再卖,呵呵)大家见谅。


作者:王文斌