步步为营 .NET 设计模式学习笔记 五、Prototype(原型模式)
概述
在软件系统中,有时候面临的产品类是动态变化的,而且这个产品类具有一定的等级结构。这时如果用工厂模式,则与产品类等级结构平行的工厂方法类也要随着这种变化而变化,显然不大合适。那么如何封装这种动态的变化?从而使依赖于这些易变对象的客户程序不随着产品类变化?
意图
在软件系统中,经常面临着“某些结构复杂的对象”的创建工作;由于需求的变化,这些对象经常面临着剧烈的变化,但是它们却拥有比较稳定一致的接口。如何应对这种变化?如何向“客户程序(使用这些对象的程序)”隔离出“这些易变对象” ,从而使得“依赖这些易变对象的客户程序”不随着需求改变而改变?
<Design Pattern>示例图
生物学例子
Prototype模式使用原型实例指定创建对象的种类。新产品的原型通常是先于全部产品建立的,这样的原型是被动的,并不参与复制它自己。一个细胞的有丝分裂,产生两个同样的细胞,是一个扮演主动角色复制自己原型的例子,这演示了原型模式。一个细胞分裂,产生两个同样基因型的细胞。换句话说,细胞克隆了自己。
依赖关系的倒置
– 抽象A直接依赖于实现细节b
抽象不应该依赖于实现细节,实现细节应该依赖于抽象。
–抽象A依赖于抽象B,实
现细节b依赖于抽象B
示例解说:
狼是很有团队精神的,每次出现都是一队狼群,这次我们举例每次四个狼组成一队出现.我们只要提供一个狼的实例,其它的都复制它,改一改属性就可以了.这种模式正是我们的原型模式.
结构图:
下面我们来创建代码:
先建Location.cs:
[Serializable] public class Location { private int _X; private int _Y; public int X { get { return _X; } set { _X = value; } } public int Y { get { return _Y; } set { _Y = value; } } public Location(int x, int y) { this.X = x; this.Y = y; } }
然后建Wolf.cs:
[Serializable] public abstract class Wolf { private string _Eat; private string _Speed; private int _Attack; private string _Weight; private Location _Position; public string Eat { get { return _Eat; } set { _Eat = value; } } public string Speed { get { return _Speed; } set { _Speed = value; } } public int Attack { get { return _Attack; } set { _Attack = value; } } public string Weight { get { return _Weight; } set { _Weight = value; } } public Location Position { get { return _Position; } set { _Position = value; } } public Wolf(string eat, string speed, int attack, string weight,Location location) { this.Eat = eat; this.Speed = speed; this.Attack = attack; this.Weight = weight; this.Position = location; } public abstract Wolf Clone(bool Deep); public abstract string ShowInfo(); }
然后建AttackWolf.cs:
[Serializable] public class AttackWolf :Wolf { private string Name; public AttackWolf(string eat, string speed, int attack, string weight,Location location):base(eat,speed ,attack,weight,location) { Name = "Attack Wolf"; } public override Wolf Clone(bool Deep) { if (Deep) //是否是深拷贝 return CreateWolf(); else return (Wolf)this.MemberwiseClone(); } private Wolf CreateWolf() { MemoryStream memoryStream = new MemoryStream(); BinaryFormatter binaryFormatter = new BinaryFormatter(); binaryFormatter.Serialize(memoryStream, this); memoryStream.Position = 0; return (Wolf)binaryFormatter.Deserialize(memoryStream); } public override string ShowInfo() { StringBuilder strBuilder = new StringBuilder(); strBuilder.AppendFormat("Name:{0},Eat:{1},Speed:{2},Attack:{3},Weight:{4},Position:X is {5},Y is {6}.", this.Name, this.Eat, this.Speed, this.Attack, this.Weight, this.Position.X, this.Position.Y); return strBuilder.ToString(); } }
然后新建WolfTeam.cs:
public class WolfTeam { public List<Wolf> GetWolfGroup(Wolf wolfPrototype) { List<Wolf> wolfGroup = new List<Wolf>(); Wolf wolfone = wolfPrototype.Clone(true); wolfone.Attack += 5; wolfone.Position.X -= 5; wolfGroup.Add(wolfone); Wolf wolftwo = wolfPrototype.Clone(true); wolftwo.Attack -= 5; wolftwo.Position.X += 5; wolfGroup.Add(wolftwo); Wolf wolfthree = wolfPrototype.Clone(true); wolfthree.Attack += 10; wolfthree.Position.Y -= 5; wolfGroup.Add(wolfthree); Wolf wolffour = wolfPrototype.Clone(true); wolffour.Attack -= 10; wolffour.Position.Y += 10; wolfGroup.Add(wolffour); return wolfGroup; } }
然后调它:
public partial class Run : Form { public Run() { InitializeComponent(); } private void btnRun_Click(object sender, EventArgs e) { Wolf newwolf = new AttackWolf("meat", "70km/h", 90, "30kg", new Location(10, 10)); WolfTeam CreatewolfTeam = new WolfTeam (); List<Wolf> wolfgroup = CreatewolfTeam.GetWolfGroup(newwolf); foreach (Wolf singwolf in wolfgroup) rtbResult.AppendText(singwolf.ShowInfo() + "\n"); } }
运行结果如图:
实现要点
1、 Prototype模式同样用于隔离类对象的使用者和具体类型(易变类)之间的耦合关系,它同样要求这些“易变类”拥有“稳定的接口”。
2、 Prototype模式对于“如何创建易变类的实体对象”用“原型克隆”的方法来做,它使得我们可以非常灵活地动态创建“拥有某些稳定接口”
的新对象——所需工作仅仅是注册一个新类的对象(即原型),然后在任何需要的地方不断地Clone。
3、 Prototype模式中的Clone方法可以利用.NET中的Object类的MemberwiseClone()方法或者序列化来实现深拷贝。
效果
1.它对客户隐藏了具体的产品类,因此减少了客户知道的名字的数目。
2.Prototype模式允许客户只通过注册原型实例就可以将一个具体产品类并入到系统中,客户可以在运行时刻建立和删除原型。
3.减少了子类构造,Prototype模式是克隆一个原型而不是请求工厂方法创建一个,所以它不需要一个与具体产品类平行的Creater类层次。
4.Portotype模式具有给一个应用软件动态加载新功能的能力。由于Prototype的独立性较高,可以很容易动态加载新功能而不影响老系统。
5.产品类不需要非得有任何事先确定的等级结构,因为Prototype模式适用于任何的等级结构
6.Prototype模式的最主要缺点就是每一个类必须配备一个克隆方法。而且这个克隆方法需要对类的功能进行通盘考虑,这对全新的类来说不是很难,但对已有的类进行改造时,不一定是件容易的事。
适用性
在下列情况下,应当使用Prototype模式:
1.当一个系统应该独立于它的产品创建,构成和表示时;
2.当要实例化的类是在运行时刻指定时,例如,通过动态装载;
3.为了避免创建一个与产品类层次平行的工厂类层次时;
4.当一个类的实例只能有几个不同状态组合中的一种时。建立相应数目的原型并克隆它们可能比每次用合适的状态手工实例化该类更方便一些。
总结
Prototype模式同工厂模式,同样对客户隐藏了对象的创建工作,但是,与通过对一个类进行实例化来构造新对象不同的是,原型模式是通过拷贝一个现有对象生成新对象的,达到了“隔离类对象的使用者和具体类型(易变类)之间的耦合关系”的目的。