上一篇博文提到,现在搭建起来的测试平台大多数情况下可以工作得不错,但是还有一个重要缺陷,每次运行的PatternTester子类对象是从testerList中获取的,而在C#中,对象是属于引用类型,赋值过程只是增加了这个对象的引用,任何一个引用上发生的改变都会反映到其他引用上,因此假设我们在客户端(Main函数)中改变了运行中的tester对象,例如令其等于null,那么testerList该对象的引用也将指向null,如果客户端再一次到testerList中取该对象,那么将会获得一个null对象,于是程序接下来的运行就进入了一个完全未知的状态。为了防止这种情况发生,可以使用原型模式轻松实现。
原型模式是指通过已经存在的对象拷贝自身(有深浅拷贝之分,并且使用不同的拷贝方式对程序影响比较大)来产生一个新的对象,新对象的请求者不需要知道具体的类型。具体到测试平台中就是每次请求PatternTester子类对象时,不是直接返回testerList中对象的引用,而是让该对象拷贝自身,然后返回这份拷贝,即将:
public PatternTester CreatePatternTester(string patternType)
{
PatternTester tester = null;
try
{
Tester =testerList[patternName];
}
catch
{
Tester = new NotInBookPatternTester();
}
retutn tester;
}
变为:
public PatternTester CreatePatternTester(string patternType)
{
PatternTester tester = null;
try
{
//Clone()函数拷贝自身
//Tester并不知道该对象的实际类型
Tester =testerList[patternName].Clone();
}
catch
{
Tester = new NotInBookPatternTester();
}
retutn tester;
}
可以看到,变化非常小,不必为每一个PatternTester子类都实现该方法,只需在父类PatternTester中增加此方法并将其设为虚函数,编写一份缺省的实现,子类可以根据需要决定是否续写此方法。而在C#中,只需让父类PatternTester类实现IClonable接口,如下:
publicabstractclass IPatternTester: ICloneable,IDisposable
{
private boolIsDisposed = false;
//实现ICloneable接口,必须实现此方法,这也是我们需要的
public Object Clone()
{
//.MemberwiseClone()是.NET提供的一个浅拷贝方法,由于
//PatternTester类并没有引用类型的数据成员,浅拷贝足矣
return (Object)this.MemberwiseClone();
}
public voidDispose()
{
IsDisposed = true;
}
protectedvirtualvoid RequireInput()
{
}
protected virtualvoid RunTest()
{
}
protected virtualvoid ShowOutput()
{
}
public voidRun()
{
if (IsDisposed ==true)
thrownewNullReferenceException();
RequireInput();
RunTest();
ShowOutput();
}
}
至此,在原型模式的帮助下,PatternTester子类实例对象在客户端的更改不再会影响到testerList中的对象,用户每次向PatternTesterFactory类请求tester实例时就可以保证得到一个符合预期的有效引用。好了,原型模式就是这么简单,但却让我们更加安全地自由地使用对象!当然原型模式的好处并不止这一个,实际上前面提到的另外三个模式也还有许多其他的好处,这会在下一篇文章里陈述。
下一篇文章会简单描述一个新的模式——工厂方法,之所以说简单地描述一下,是因为工厂方法和简单工厂非常相似,而由于C#语言的特殊性,使得这种相似性又增加不少。描述完工厂方法后,将对这五个模式进行总结并谈谈自己的感受。