设计模式(五)—— 原型模式
模式简介
用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
通俗来说,原型模式就是从一个对象,通过复制的手段去创建另外一个对象,而且不需要知道任何创建的细节。
思考:这么做有什么好处?
- 省略创建者类。例如在工厂方法中,客户端创建一个产品,必须调用相应工厂类中的方法获取产品实例。在原型模式中,原型本身提供Clone方法,因此原型的实例扮演了创建者和产品双重角色。
- 减少创建实例带来的成本。这里所说的“成本”包括两个层面:系统性能的消耗及创建实例的代码量。当初始化一个对象带来大量的性能消耗,需要创建多个实例且各个实例之间差别不大时,原型模式通过复制原型的方式减小系统性能的消耗。另一方面,当初始化对象需要大量繁杂的代码,使用原型模式避免了代码的重复。
结构说明
UML类图
角色说明
- Prototype
抽象原型类。声明一个克隆自身的接口(抽象方法)
- ConcretePrototype
具体原型类。实现一个克隆自身的方法
- Client
让一个原型克隆自身从而创建一个新的对象
示例分析
下面我们来看一个创建颜色Color对象的示例:
创建原型类Color(本示例省略了抽象原型类,改用ICloneable接口代替)
class Color : ICloneable
{
private int red;
private int green;
private int yellow;
public Color(int red,int green,int yellow)
{
this.red = red;
this.green = green;
this.yellow = yellow;
}
public object Clone()
{
Console.WriteLine($"Cloning color RGB: {red},{green},{yellow}");
return this.MemberwiseClone() as Color;
}
}
创建原型管理器ColorManager
class ColorManager
{
Dictionary<string, Color> colors = new Dictionary<string, Color>();
public Color this[string key]
{
get { return colors[key]; }
set { colors.Add(key, value); }
}
}
客户端向原型管理器注册,获取Color对象,以及动态注册原型
static void Main(string[] args)
{
ColorManager colorManager = new ColorManager();
//向原型管理器注册原型
colorManager["red"] = new Color(255, 0, 0);
colorManager["green"] = new Color(0, 255, 0);
colorManager["yellow"] = new Color(0, 0, 255);
//创建实例
Color red = colorManager["red"].Clone() as Color;
//动态注册原型
Console.WriteLine("Start to register prototype : ");
Console.WriteLine("Key : ");
string key = Console.ReadLine();
Console.WriteLine("RGB[example:255,54,0]:");
string[] input = Console.ReadLine().Split(',');
int[] rgb = Array.ConvertAll<string, int>(input, p => int.Parse(p));
colorManager[key] = new Color(rgb[0], rgb[1], rgb[2]);
Color myColor = colorManager[key].Clone() as Color;
Console.ReadLine();
}
输出结果
优缺点
优点
-
对客户隐藏了具体的产品类,减少了客户知道的产品的数目
-
客户可以在运行时刻增加和删除产品
-
改变值以指定新对象,例如通过为一个对象变量指定不同的值并注册为对象的原型
-
改变结构以指定新对象
-
减少子类的构造
-
用类动态配置应用
缺点
- 每个Prototype子类都必须实现Clone操作,有时会比较困难
浅拷贝与深拷贝
浅拷贝
拷贝一个对象时,仅拷贝对象的引用,拷贝的对象和源对象引用同一份实体。也就是说,改变其中一个对象会影响到另一个对象的状态。
深拷贝
拷贝一个对象时,不仅拷贝对象的引用,还将对象引用的值一同拷贝。这样拷贝的对象和源对象相互独立,其中一个改变不会影响另外一个对象。
使用场景
当一个系统应该独立于它的产品创建、构成和表示时,要使用Prototype模式;以及
- 当要实例化的类是在运行时刻指定时;或者
- 为了避免创建一个与产品类层次平行的工厂类层次时;或者
- 当一个类的实例只能有几个不同状态组合中的一种时