您的位置:首页 > 其它

设计模式之——Prototype Pattern(原型模式)

2008-11-20 17:42 302 查看
原型模式隶属创建型模式,创建型模式目标是从系统中分离出对象如何被创建,组装,表现的逻辑。
即该类模式用于分离创建过程与使用过程。
与创建相关的W:
what(什么——子类?)。
who(谁——创建的主语实体,对象不一定非的自己创建自己,自身成员(所依赖的对象)可以由别的对象提供,比如依赖注入,或是从一个上下文中查找(前提是它注册过))。
how(如何——是纯粹的内存构造吗?从缓存获取,还是从文件中解析,还是从网络读取,或是从数据库加载,还是使用反射?),
When(时机——是立即构造,还是延迟构造)。
创建型模式会隐藏以上知识。
因为系统总是由对象搭建,每一个对象其方法的实现涉及到其依赖对象的协作。而我们总是硬编码对象的创建过程,比如 new 或 lookup()。如果解耦对象的创建与使用,系统会更具弹性。因为把创建逻辑分离到一独立的地方,再借助抽象耦合(依赖于抽象),我们可以创建一个被依赖类的子类的实例,而不影响系统的工作。通过XML配置创建一个实例再今天已不是新鲜的事情了!
比如: A.SomeMethod(){
ISomeType coadjutantObj=(ISomeType)Context.GetObjet(SomeType);
coadjutantObj.someMethod();
}

*

**
原型模式,确保何时(when)进行复杂对象的拷贝。

任务:
原型模式通过从一些已存在的原型对象中拷贝一个对象来创建新对象。
原型模式有两个优点:加速初始化一个非常大,或是动态加载的类(此时使用拷贝要快些);其持有一个大的数据结构的可标示部分的一个记录,拷贝时不知道它到底由哪个子类创建(由于耦合于抽象的缘故)。
**

***
应用示例:
使用原型模式可完成回滚机制,CallBack。拷贝一个原始对象,再拷贝对象上做操作,当操作失效/失败时当前对象的引用指向原始对象,提交(Commit)时,当前引用指向拷贝对象。

也可用此思想来实现归档(Archive)。
***

****
设计:
对象一般实例化自一个类。原型模式提出了一种创建对象的可替代方案——从已存在的原型对象创建,而非类。
UML图示:




假设:程序创建所需类型的对象,不是通过初始化而是通过拷贝一个该类既存实例。拷贝/克隆的过程可不断重复。模式的意图是这些拷贝可被更改但不会影响原始的原型,(相当于盗版过程中不会影响母带)。在程序的运行期间,新的原型可被加入进来,这些原型或来自新类(子类),或来自既存原型的变体。尽管可有多种其他的设计,但是最灵活的设计是用一个原型管理器(prototype manager)对象来维护一个原型的索引列表。该模式各角色的职责如:

1.IPrototype(原型接口):为原型定义一个接口,所有原型类实现其中的Clone( )方法。

2.Prototype(原型类):拥有克隆能力的类,它实现IPrototype接口定义的方法。

3.PrototypeManager(原型管理器):维护一个克隆类型的列表和其键值。

4.Client(客户类):将原型类加至列表中,并可请求克隆。
****
*****
C#中:
MemberwiseClone()方法对所有对象都可用。该方法拷贝(this)对象的所有域和引用,并返回一个该拷贝对象的引用。然而对于引用,它并不拷贝引用所指向的对象,仅拷贝引用本身(引用本质上是一个安全的指针——即一个内存地址而已)。就是说它仅执行被称为“浅拷贝”的行为。许多对象都是简单对象,并不含引用成员,(简单对象成员,是一些常规的值对象:如int,double,float等)。对简单对象浅拷贝足够了。为了保存完全的一个对象的值,包括其子对象,我们使用“深拷贝”。深拷贝一般使用递归闭包的赋值完成,就是说对一个对象要返回其深拷贝对象,要在该对象树上遍历调用每个子成员的拷贝方法,针对引用子对象仍要调用其深拷贝方法,深拷贝方法在值对象上与浅拷贝实现算法一样。伪算法如下:
//遍历一个对象的所有成员
foreach (member in Object.members){
if(member instanceOf ValueType){
//如果该成员是值类型
//克隆成员等于该成员
mClone=member;
}
else{
//该成员是引用成员
//递归调用该成员的深拷贝方法
mClone=member.DeepClone();
}
cloneObject.setMemberValue(member.name,mClone);
}
这样会要求所有的引用型子对象或子子对象类都有一个同名的深拷贝方法。这种思想与.net的Dispose模式一样。你需要手动调用每个成员的深拷贝方法,并且不能遗忘任何一个。

写一个跟踪一个结构中的所有连接的通用算法并不容易,但是确实存在这样的算法。.net框架中它被封装在一个称为serialization(序列化)的过程之中。对象可被拷贝到一个给定的目的地,如果你愿意你还可以复原,目的地有很多(如磁盘,文件,数据库,网络等),对小对象这个目的地还可以是内存,所以一个深拷贝方法可由序列化和反序列化组成(serializing and deserializing) 。

使一个类成为可序列化的,只需要使用c#的[Serializable( )]特性标记该类即可。不像java中你还要实现Serializable接口,尽管该接口没有任何方法,它只是一个标识而已但相比而言。.net的特性标记简单多了。
序列化是.net框架的一部分,不是c#语言特有的,每个面向.net的语言都提供了相应手段支持序列化。

序列化一个对象结构,仅当其成员的所有引用对象都是序列化的时候才能真正成功。避免序列化一个含有指向“资源”的引用成员的对象。比如,引用成员指向一个数据库连接,或是一个文件句柄等。

*****

******
c#3.0Design Patterns一书已经实现的足够好了,这里借用:
实现:
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;

namespace PrototypePattern {

[Serializable( )]
public abstract class IPrototype <T> {

// 浅拷贝
public T Clone( ) {
return (T) this.MemberwiseClone( );
}

// 深拷贝实现
public T DeepCopy( ) {
MemoryStream stream = new MemoryStream( );
BinaryFormatter formatter = new BinaryFormatter( );
formatter.Serialize(stream, this);
stream.Seek(0, SeekOrigin.Begin);
T copy = (T) formatter.Deserialize(stream);
stream.Close( );
return copy;
}
}
}

using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using PrototypePattern;

[Serializable( )]
// 帮助类用于创建第二级数据结构
class DeeperData {
public string Data {get; set;}

public DeeperData(string s) {
Data = s;
}
public override string ToString ( ) {
return Data;
}
}

[Serializable( )]
class Prototype : IPrototype <Prototype> {

// 内容成员
public string Country {get; set;}
public string Capital {get; set;}
public DeeperData Language {get; set;}

public Prototype (string country, string capital, string language) {
Country = country;
Capital = capital;
Language = new DeeperData(language);
}

public override string ToString( ) {
return Country+"/t/t"+Capital+"/t/t->"+Language;
}

}
[Serializable( )]
class PrototypeManager : IPrototype <Prototype> {
public Dictionary <string, Prototype> prototypes
= new Dictionary <string, Prototype> {
{"Germany",
new Prototype ("Germany", "Berlin", "German")},
{"Italy",
new Prototype ("Italy", "Rome", "Italian")},
{"Australia",
new Prototype ("Australia", "Canberra", "English")}
};
}

class PrototypeClient : IPrototype <Prototype> {

static void Report (string s, Prototype a, Prototype b) {
Console.WriteLine("/n"+s);
Console.WriteLine("Prototype "+a+"/nClone "+b);
}

static void Main ( ) {

PrototypeManager manager = new PrototypeManager( );
Prototype c2, c3;

c2 = manager.prototypes["Australia"].Clone( );
Report("Shallow cloning Australia/n===============",
manager.prototypes["Australia"], c2);

c2.Capital = "Sydney";
Report("Altered Clone's shallow state, prototype unaffected",
manager.prototypes["Australia"], c2);

c2.Language.Data = "Chinese";
Report("Altering Clone deep state: prototype affected *****",
manager.prototypes["Australia"], c2);

c3 = manager.prototypes["Germany"].DeepCopy( );
Report("Deep cloning Germany/n============",
manager.prototypes["Germany"], c3);

c3.Capital = "Munich";
Report("Altering Clone shallow state, prototype unaffected",
manager.prototypes["Germany"], c3);

c3.Language.Data = "Turkish";
Report("Altering Clone deep state, prototype unaffected",
manager.prototypes["Germany"], c3);
}
}

所有涉及原型的类都需加上Serializable( )特性。
******
其实模式具体如何实现不是太重要,主要是要抓住“神”。这里实现核心就是深拷贝如何实现的问题,不过抽象的IPrototype <T>模板类已经完美解决了(多亏了序列化与反序列化),你只需要继承它就行了!
我们使用原型模式的目的:

对客户隐藏具体的类;
运行期间增加或删除新类(通过原型);
保持系统的类数量最少;
在运行时调整数据结构。

使用此模式的时机:
与组合模式一起,提供归档功能。
当子类开始泛滥增生时,用该模式替代工厂方法模式,
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: