原型模式
一、完成者信息
姓名:赖春林
学号:07770110
二、模式信息
1、模式名称:原型模式
2、模式概述:
在软件系统中,有时候面临的产品类是动态变化的,而且这个产品类具有一定的等级结构。这时如果用工厂模式,则与产品类等级结构平行的工厂方法类也要随着这种变化而变化,显然不大合适。那么如何封装这种动态的变化?从而使依赖于这些易变对象的客户程序不随着产品类变化?
3、定义:
用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
4、生活场景:
超市销售不同品牌的可乐,每种可乐的数量随着销售的进行而动态变化,进货数量也是动态的不可预知的,并且超市需要销售哪种类型的可乐可能也会变化。
终极目标:销售多种数量不定的可乐。
4.1. 不假思索的思路:
先建立一个可乐类,然后派生出可口可乐、鲜橙多等可乐类,如下图:
实现代码:
public abstract class Coke
{
public abstract void Display();
}
public class Coca_Cola:Coke
{
public override void Display()
{
Console.WriteLine("可口可乐,500ml,瓶装");
}
}
public class xianchengduo : Coke
{
public override void Display()
{
Console.WriteLine("鲜橙多,500ml,瓶装");
}
}
然后需要哪种可乐就声明哪种可乐的对象,因为超市中需要每种可乐的数量不止一个,因此应该声明对象数组。但是这样的设计很明显没有达到封装变化点的目的,并且可乐的种类很多会造成子类数目的膨胀,对象数组的增多及数量的动态变化造成对象数组长度的难于确定。
4.2. 归纳阶段
针对上述问题,这里提出的解决方法是采用原型模式,使用原型模式时务必清楚原型模式不仅仅是有些教材上讲的拷贝那么简单。实际上原型模式有两成含义即:①用原型实例指定创建对象的种类;②通过复制这些原型创建新的对象。接下来我们的工作就是围绕这两层含义进行展开。首先我们可以通过CokeManage这个类来注册可乐原型,接着超市需要哪种可乐就用哪种可乐的原型来克隆自己,需要多少就克隆多少,这样就达到动态创建对象而客户程序又不随着产品类而变化的目的。如下图:
实现代码:
public abstract class CokePrototype
{
public abstract CokePrototype Clone();
}
public class ConcreteCoke:CokePrototype
{
private string _name;
private int _volume;
private string _state;
public ConcreteCoke(string name,int volume,string state)
{
this._name = name;
this._volume = volume;
this._state = state;
}
public override CokePrototype Clone()
{
return (CokePrototype)this.MemberwiseClone();
}
public void Display()
{
Console.WriteLine("可乐名称:"+_name);
Console.WriteLine("容积:" + _volume);
Console.WriteLine("包装:" + _state);
Console.WriteLine("\n");
}
}
/// <summary>
/// 原型管理者
/// </summary>
class CokeManager
{
Hashtable coke = new Hashtable();
public CokePrototype this[string name]
{
get { return (CokePrototype)coke[name]; }
set { coke.Add(name, value); }
}
}
class Program
{
static void Main(string[] args)
{
CokeManager cokemanager = new CokeManager();
//通过原型实例创建对象的种类
cokemanager["可口可乐"] = new ConcreteCoke("可口可乐",500,"瓶装");
cokemanager["鲜橙多"] = new ConcreteCoke("鲜橙多", 500, "瓶装");
//通过拷贝这些原型创建新的对象
string cokeName = "可口可乐";
ConcreteCoke coke1 = (ConcreteCoke)cokemanager[cokeName].Clone();
coke1.Display();
//创建第二个同样的对象
ConcreteCoke coke2 = (ConcreteCoke)coke1.Clone();
coke2.Display();
Console.ReadLine();
}
}
4.3浅拷贝与深度拷贝:
上例中实现的是浅拷贝,浅拷贝拷贝的是对象的引用而没有拷贝对象所拥有的变量。要实现深度拷贝则可通过序列化的方式实现。现在假设瓶子也是一个对象,则上例上拷贝的仅仅是瓶子这个对象的引用,瓶子的参数并没有拷贝。代码实现时抽象类及具体类都必须标注为可序列化的[Serializable],下面是对上例实现深度拷贝的源代码:
public class bottle
{
public string _material;
public bottle(string material)
{
this._material = material;
}
}
[Serializable]
public abstract class CokePrototype
{
public abstract CokePrototype Clone();
}
[Serializable]
public class ConcreteCoke:CokePrototype
{
private string _name;
private int _volume;
private string _state;
public ConcreteCoke(string name,int volume,string state)
{
bottle bot = new bottle(name);
this._name = bot._material;
this._volume = volume;
this._state = state;
}
public override CokePrototype Clone()
{
CokePrototype cokePrototype;
MemoryStream memoryStream = new MemoryStream();
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(memoryStream,this);
memoryStream.Position = 0;
cokePrototype = (CokePrototype)formatter.Deserialize(memoryStream);
return cokePrototype;
}
public void Display()
{
Console.WriteLine("可乐名称:"+_name);
Console.WriteLine("容积:" + _volume);
Console.WriteLine("包装:" + _state);
Console.WriteLine("\n");
}
}
/// <summary>
/// 原型管理者
/// </summary>
class CokeManager
{
Hashtable coke = new Hashtable();
public CokePrototype this[string name]
{
get { return (CokePrototype)coke[name]; }
set { coke.Add(name, value); }
}
}
class Program
{
static void Main(string[] args)
{
CokeManager cokemanager = new CokeManager();
//通过原型实例创建对象的种类
cokemanager["可口可乐"] = new ConcreteCoke("可口可乐",500,"瓶装");
cokemanager["鲜橙多"] = new ConcreteCoke("鲜橙多", 500, "瓶装");
//通过拷贝这些原型创建新的对象
string cokeName = "可口可乐";
ConcreteCoke coke1 = (ConcreteCoke)cokemanager[cokeName].Clone();
coke1.Display();
//创建第二个同样的对象
ConcreteCoke coke2 = (ConcreteCoke)coke1.Clone();
coke2.Display();
Console.ReadLine();
}
}
5、验证阶段:
当前目标: 添加新的可乐名称如雪碧。
结果为:
只需在Program类中注册一个雪碧的原型即cokemanager["雪碧"] = new ConcreteCoke("雪碧", 600, "瓶装");然后需要用到雪碧时用该原型创建一个新对象即可。
6、抽象阶段:
思路描述:动态保存当前对象的状态,随时使用对象流保存当前对象的一个复制品。
类结构图: