第十二课 原型模式
定义:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
上面就是原型模式的定义了。那么为什么我们要采用原型模式呢?设想一下,现在有这么一个东西,其实可以把它抽象出一个类,但是由于他属性有着千变万化,而每种变化可能都是我们所需要的。那么解决方案呢?我想大家能马上想到以下方式:
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对象的方法来管理原型对象。如下图。
ColorTool就是一个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月初有个日语二级考试,最近可能需要复习一下,而且最近领导抓一些事情,可能更新速度会有所降低。(其实主要我也要现学再卖,呵呵)大家见谅。
作者:王文斌