Prototype 原型(创建型模式)
依赖关系的倒置
抽象不应该依赖于实现细节,实现细节应该依赖于抽象。
– 抽象A直接依赖于实现细节b
–抽象A依赖于抽象B,实现细节b依赖于抽象B
动机(Motivation)
在软件系统中,经常面临着“某些结构复杂的对象”的创建工作;由于需求的变化,这些对象经常面临着剧烈的变化,但是它们却拥有比较稳定一致的接口。
如何应对这种变化?如何向“客户程序(使用这些对象的程序)”隔离出“这些易变对象” ,从而使得“依赖这些易变对象的客户程序”不随着需求改变而改变?
意图(Intent)
使用原型实例指定创建对象的种类,然后通过拷贝这些原型来创建新的对象。——《设计模式》GoF
结构(Structure)
实例代码
代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
namespace testWpf
{
/// <summary>
/// 抽象类,虽然没有声明成abstract,或者这里称为高层类,这是为了共享Clone方法
/// </summary>
[Serializable]
public class NormalActor
{
//跟.Net的Clone不一样
public virtual NormalActor Clone()
{
/*
* MemberwiseClone 方法创建一个浅表副本,具体来说就是创建一个新对象,然后将当前对象的非静态字段复制到该新对象。
* 如果字段是值类型的,则对该字段执行逐位复制。如果字段是引用类型,则复制引用但不复制引用的对象;
* 因此,原始对象及其复本引用同一对象。
* 因此这里使用了对象序列化和反序列化的方式得到一个深拷贝对象
*/
MemoryStream stream = new MemoryStream();
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(stream, this);
stream.Position = 0;
return formatter.Deserialize(stream) as NormalActor;
}
}
/// <summary>
/// 抽象类,虽然没有声明成abstract,或者这里称为高层类,这是为了共享Clone方法
/// </summary>
[Serializable]
public class FlyActor
{
//跟.Net的Clone不一样
public virtual FlyActor Clone()
{
MemoryStream stream = new MemoryStream();
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(stream, this);
stream.Position = 0;
return formatter.Deserialize(stream) as FlyActor;
}
}
/// <summary>
/// 抽象类,虽然没有声明成abstract,或者这里称为高层类,这是为了共享Clone方法
/// </summary>
[Serializable]
public class WaterActor
{
//跟.Net的Clone不一样
public virtual WaterActor Clone()
{
MemoryStream stream = new MemoryStream();
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(stream, this);
stream.Position = 0;
return formatter.Deserialize(stream) as WaterActor;
}
}
/// <summary>
/// 虽然父类NormalActor已经是声明了可序列化标识“[Serializable]”,
/// 但“[Serializable]”是不可继承的,所以子类如果要可以序列化,则还是要加上[Serializable]标识
/// </summary>
[Serializable]
public class NormalActorA : NormalActor
{
}
[Serializable]
public class NormalActorB : NormalActor
{
}
[Serializable]
public class FlyActorA : FlyActor
{
}
[Serializable]
public class FlyActorB : FlyActor
{
}
[Serializable]
public class WaterActorA : WaterActor
{
}
[Serializable]
public class WaterActorB : WaterActor
{
}
public class GameSystem
{
public NormalActor normalActor;
public FlyActor flyActor;
public WaterActor waterActor;
public void Run()
{
NormalActor normalActor1 = normalActor.Clone();
NormalActor normalActor2 = normalActor.Clone();
NormalActor normalActor3 = normalActor.Clone();
NormalActor normalActor4 = normalActor.Clone();
NormalActor normalActor5 = normalActor.Clone();
FlyActor flyActor1 = flyActor.Clone();
FlyActor flyActor2 = flyActor.Clone();
WaterActor waterActor1 = waterActor.Clone();
WaterActor waterActor2 = waterActor.Clone();
}
}
public class App
{
public static void Main()
{
GameSystem gameSystem = new GameSystem();
gameSystem.normalActor = new NormalActorA();
gameSystem.flyActor = new FlyActorB();
gameSystem.waterActor = new WaterActorA();
gameSystem.Run();
}
}
}
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
namespace testWpf
{
/// <summary>
/// 抽象类,虽然没有声明成abstract,或者这里称为高层类,这是为了共享Clone方法
/// </summary>
[Serializable]
public class NormalActor
{
//跟.Net的Clone不一样
public virtual NormalActor Clone()
{
/*
* MemberwiseClone 方法创建一个浅表副本,具体来说就是创建一个新对象,然后将当前对象的非静态字段复制到该新对象。
* 如果字段是值类型的,则对该字段执行逐位复制。如果字段是引用类型,则复制引用但不复制引用的对象;
* 因此,原始对象及其复本引用同一对象。
* 因此这里使用了对象序列化和反序列化的方式得到一个深拷贝对象
*/
MemoryStream stream = new MemoryStream();
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(stream, this);
stream.Position = 0;
return formatter.Deserialize(stream) as NormalActor;
}
}
/// <summary>
/// 抽象类,虽然没有声明成abstract,或者这里称为高层类,这是为了共享Clone方法
/// </summary>
[Serializable]
public class FlyActor
{
//跟.Net的Clone不一样
public virtual FlyActor Clone()
{
MemoryStream stream = new MemoryStream();
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(stream, this);
stream.Position = 0;
return formatter.Deserialize(stream) as FlyActor;
}
}
/// <summary>
/// 抽象类,虽然没有声明成abstract,或者这里称为高层类,这是为了共享Clone方法
/// </summary>
[Serializable]
public class WaterActor
{
//跟.Net的Clone不一样
public virtual WaterActor Clone()
{
MemoryStream stream = new MemoryStream();
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(stream, this);
stream.Position = 0;
return formatter.Deserialize(stream) as WaterActor;
}
}
/// <summary>
/// 虽然父类NormalActor已经是声明了可序列化标识“[Serializable]”,
/// 但“[Serializable]”是不可继承的,所以子类如果要可以序列化,则还是要加上[Serializable]标识
/// </summary>
[Serializable]
public class NormalActorA : NormalActor
{
}
[Serializable]
public class NormalActorB : NormalActor
{
}
[Serializable]
public class FlyActorA : FlyActor
{
}
[Serializable]
public class FlyActorB : FlyActor
{
}
[Serializable]
public class WaterActorA : WaterActor
{
}
[Serializable]
public class WaterActorB : WaterActor
{
}
public class GameSystem
{
public NormalActor normalActor;
public FlyActor flyActor;
public WaterActor waterActor;
public void Run()
{
NormalActor normalActor1 = normalActor.Clone();
NormalActor normalActor2 = normalActor.Clone();
NormalActor normalActor3 = normalActor.Clone();
NormalActor normalActor4 = normalActor.Clone();
NormalActor normalActor5 = normalActor.Clone();
FlyActor flyActor1 = flyActor.Clone();
FlyActor flyActor2 = flyActor.Clone();
WaterActor waterActor1 = waterActor.Clone();
WaterActor waterActor2 = waterActor.Clone();
}
}
public class App
{
public static void Main()
{
GameSystem gameSystem = new GameSystem();
gameSystem.normalActor = new NormalActorA();
gameSystem.flyActor = new FlyActorB();
gameSystem.waterActor = new WaterActorA();
gameSystem.Run();
}
}
}
Prototype模式的几个要点
- Prototype模式同样用于隔离类对象的使用者和具体类型(易变类)之间的耦合关系,它同样要求这些“易变类”拥有“稳定的接口”。
- Prototype模式对于“如何创建易变类的实体对象”采用“原型克隆”的方法来做,它使得我们可以非常灵活地动态创建“拥有某些稳定接口”的新对象——所需工作仅仅是注册一个新类的对象(即原型),然后在任何需要的地方不断地Clone。
- Prototype模式中的Clone方法可以利用.NET中的Object类的MemberwiseClone()方法或者序列化来实现深拷贝。
有关创建性模式的讨论
- Singleton模式解决的是实体对象个数的问题。除了Singleton之外,其他创建型模式解决的都是new所带来的耦合关系。
- Factory Method, Abstract Factory, Builder都需要一个额外的工厂类来负责实例化“易变对象”,而Prototype则是通过原型(一个特殊的工厂类)来克隆“易变对象”。
- 如果遇到“易变类”,起初的设计通常从FactoryMethod开始,当遇到更多的复杂变化时,再考虑重构为其他三种工厂模式( Abstract Factory,Builder , Prototype )。
最后谈谈使用Prototype模式需要使用到的两个技术问题:MemberwiseClone与Clone,[Serializable]属性能否继承的问题
一、MemberwiseClone与Clone
MemberwiseClone 方法创建一个浅表副本,具体来说就是创建一个新对象,然后将当前对象的非静态字段复制到该新对象。如果字段是值类型的,则对该字段执行逐位复制。如果字段是引用类型,则复制引用但不复制引用的对象;因此,原始对象及其复本引用同一对象。
下面的代码就是演示这个问题:
代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
namespace testWpf
{
[Serializable]
class DemoClass
{
public int i = 0;
public int[] iArr = { 1, 2, 3 };
public DemoClass Clone1()
{
return this.MemberwiseClone() as DemoClass;
}
public DemoClass Clone2()
{
MemoryStream stream = new MemoryStream();
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(stream, this);
stream.Position = 0;
return formatter.Deserialize(stream) as DemoClass;
}
}
class Program
{
static void Main(string[] args)
{
DemoClass a = new DemoClass();
a.i = 10;
a.iArr = new int[] { 8, 9, 10 };
DemoClass b = a.Clone1();
DemoClass c = a.Clone2(); // 更改 a 对象的iArr[0], 导致 b 对象的iArr[0] 也发生了变化
a.iArr[0] = 88;
Console.WriteLine("MemberwiseClone");
Console.WriteLine(b.i);
foreach (var item in b.iArr)
{
Console.WriteLine(item);
}
Console.WriteLine("Clone2");
Console.WriteLine(c.i);
foreach (var item in c.iArr)
{
Console.WriteLine(item);
}
Console.ReadLine();
}
}
}
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
namespace testWpf
{
[Serializable]
class DemoClass
{
public int i = 0;
public int[] iArr = { 1, 2, 3 };
public DemoClass Clone1()
{
return this.MemberwiseClone() as DemoClass;
}
public DemoClass Clone2()
{
MemoryStream stream = new MemoryStream();
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(stream, this);
stream.Position = 0;
return formatter.Deserialize(stream) as DemoClass;
}
}
class Program
{
static void Main(string[] args)
{
DemoClass a = new DemoClass();
a.i = 10;
a.iArr = new int[] { 8, 9, 10 };
DemoClass b = a.Clone1();
DemoClass c = a.Clone2(); // 更改 a 对象的iArr[0], 导致 b 对象的iArr[0] 也发生了变化
a.iArr[0] = 88;
Console.WriteLine("MemberwiseClone");
Console.WriteLine(b.i);
foreach (var item in b.iArr)
{
Console.WriteLine(item);
}
Console.WriteLine("Clone2");
Console.WriteLine(c.i);
foreach (var item in c.iArr)
{
Console.WriteLine(item);
}
Console.ReadLine();
}
}
}
二、[Serializable]属性能否继承的问题
代码
[Serializable]
public abstract class CSMessage : MessageBase
{
private string userName;
protected CSMessage(string anUserName)
{
userName = anUserName;
}
public string UserName
{
get { return userName; }
}
}
[Serializable]
public class LoginMessage : CSMessage
{
private string password;
public LoginMessage(string userName, string password)
: base(userName)
{
this.password = password;
}
public string Password
{
get { return password; }
}
}
public abstract class CSMessage : MessageBase
{
private string userName;
protected CSMessage(string anUserName)
{
userName = anUserName;
}
public string UserName
{
get { return userName; }
}
}
[Serializable]
public class LoginMessage : CSMessage
{
private string password;
public LoginMessage(string userName, string password)
: base(userName)
{
this.password = password;
}
public string Password
{
get { return password; }
}
}
代码如上,测试发现类属性是不可以继承的,仔细推敲MSDN上关于对类属性是说明,类属性是一种标记当代码被编译为MSIL后由CLR根据类属性标记为其它附上相关的特性,唉。。不仔细看还是很容易弄错的