本帖介绍 Prototype Pattern (原型模式),并以一个「人事招聘程序」作为示例来说明。
--------------------------------------------------------
本帖的示例下载点:
http://files.cnblogs.com/WizardWu/090713.zip
第一个示例为 Console Mode (控制台应用程序) 项目,第二个示例为 ASP.NET 网站项目。
执行示例需要 Visual Studio 2008 或 IIS + .NET 3.0,不需要数据库。
--------------------------------------------------------
Prototype Pattern (原型模式)
Specify the kinds of objects to create using a prototypical instance, and create new objects by copying this prototype.
- Design Patterns: Elements of Reusable Object-Oriented Software
原型模式就是以一个既有的原型实例当作范本,利用复制的方式,动态获得这个原型实例的状态以及全部的字段和属性,以此创建一或多个相同的对象,而且不需要知道任何创建的细节。
Prototype 模式打个通俗的比方:假如您在图书馆看到几本自己喜欢的书籍,当看到某些知识点时,想在上面作相关记号,但由于其是图书馆的书,不能在上面乱涂乱画。此时您只好把相关的章节,用复印机把它复印出来,然后在自己复印的纸张上作记号。在 Prototype Pattern 里,Clone 方法就如同此种复印的动作,用户从一个既有的原型实例 (如同图书馆里的书),复印后得到一或多个新的拷贝,不会破坏原本的原型,且用户不必知道原型的内容和格式。
Prototype 也具有一种「展示」的意味,就像是车展上的「原型」车款。当您对某个车款感兴趣时,您可购买相同的车款,而不是车展上展示的那辆车。
在软件设计方面,也常需要进行此种对象复制。例如我们要写一套室内设计软件,软件的操作界面上有一条 Toolbar,用户只要单击 Toolbar 上的 Button,或用鼠标拖曳到设计窗格中,就可创建一个桌子或椅子的副本,并可事后改变它的颜色或位置。当设计师改变设计图中的副本对象时,Toolbar 上的「原型」对象并不会跟着被改变。同样的观念,亦适用于工业设计 CAD 软件、图像处理软件,以及 Visual Studio 等各种软件的设计。
Prototype 模式的重点在于 Clone 方法,它负责复制 (克隆) 出一个新的对象并返回,而不是用 new 运算符和某个类的构造函数去创建实例。在此模式中,派生类如何覆写父类的 Clone 方法将是重点。而 clone 的方式,又可分为「浅拷贝 (shallow copy)」和「深拷贝 (deep copy)」,在介绍这个 Prototype 模式之前,先简单介绍一下这两种拷贝方式的差异 [1], [2], [3], [4] :
- 浅拷贝; 浅表复制 (shallow copy):对象拷贝时,如果字段是「值类型 (Value Type)」,则直接复制其值 (亦即复制整个字段);若字段为「引用类型 (Reference Type)」,则只复制其「引用 (reference; pointer)」,但不复制引用的字段,亦即若更改了任一个副本对象的某一个「引用类型」字段,则原型正本对象、其他副本对象,也全部会一并更改 (如同本帖的第三个示例 02_Employee / 02_ShallowCopy_fail.aspx.cs),也就是说正本和所有的副本,都指向了内存的同一个位置。
- 深拷贝; 深层复制 (deep copy):不论对象的字段为「值类型」或「引用类型」,都会完整地复制,而且这些字段和属性都是完全独立的。在深拷贝中,所有的对象都是重复的。
另补充,.NET 的类型系统,分为「值类型」、「引用类型」两种,其对象在内存中的存储方式不同,如下:
- 值类型:只需要一段单独的内存,用于存储实际的数据在「栈 (Stack)」里,例如:int、byte、float、double、bool、struct、enum、char、...等类型。
- 引用类型:需要两段内存,第一段存储实际的数据,其总是位于「堆 (Heap)」中;第二段是一个存在「栈」里的引用 (reference; pointer),其指向数据在「堆」中的实际存放位置,例如:object、string、class (包括自定义类)、interface、delegate、array (参考本帖的第三、第四个示例) 等类型。
但 string (字符串) 较特殊。string 虽然是「引用类型」,但却拥有「值类型」的特性。在 Prototype Pattern 及本帖的四个示例中,当透过 MemberwiseClone 方法做「浅拷贝」时,对象的 string 字段仍会被完整地复制,其结果就如同 int 等「值类型」的字段一样。
如下图 1 及下方示例 01_Shell,我们可透过自定义的 Prototype 抽象类,搭配 .NET 最顶层基类 System.Object 的 MemberwiseClone 方法,达成对象的「浅拷贝」,亦即复制某个对象其所有「字段 (field)」的值;但在 .NET 中,亦可舍弃此一自定义抽象类,让图 1 中的 ConcretePrototype1 类、ConcretePrototype2 类,改为实现 .NET 原生的 System.ICloneable 接口,透过实现此接口唯一的一个 Clone 方法,来达成对象的「浅拷贝」或「深拷贝」。
图 1 此图为 Prototype 模式的经典类图
上方图 1 的 Class Diagram,以及「Shell (壳)」示例中,客户端程序透过抽象类 Prototype 的定义来操作其派生类。先选择一个「原型」实例,亦即 ConcretePrototype1 类或 ConcretePrototype2 类的实例,通过调用它所覆写抽象父类的 Clone 方法,获得一个和它一样、有相同 id 值的新对象,而非透过 new 运算符去创建实例。而拷贝完成后,拷贝的原型样本 (p1、p2),和副本 (c1、c2) 是两个独立的对象,可以独立变化和修改。
但为什么不用 new 运算符加上某个类的构造函数去创建实例,而要再衍生出这种 Prototype Pattern 呢?其中一个原因,是系统设计上,类的种类可能会很多,而难以整合成特定的自定义类时;或类与类之间有大量平行阶层结构,类的数量过多会造成管理上的困难。例如本帖一开始提到的室内设计软件,桌子类和椅子类,又可各分成: 方形的、圆形的、其他各种形状的…,若全部都要写成不同的类,类的数量会很可观。
此外,也是为了避免整个系统中,类与类之间结构的剧烈变化,避免为了重载一个新的函数,导致我们要修改的不是一个类,而是整个继承关系体系里的每一个类。
另一个原因,是用户在用鼠标操作这个室内设计软件时 (运行时期),若要重复创建相同的圆形桌子对象时,用 Prototype Pattern 这种对象复制的方式,由于对象初始化的内容都相同,会比从头用类去创建新的实例要容易,同时能在客户端程序中隐藏对象创建的细节,且在速度和性能上会较优 [15], [17]。
接下来的三个示例,为一个人事招聘系统的部分代码,我们以此为例来实现原型模式。某间公司要招聘「程序员」、「行政文员」两种职务,其中的 Employee 为顶层的抽象类,两个派生类 Developer 和 Typist 必须实现其 Clone 方法。示例执行结果如下图 3。
由于两个派生类 Developer 和 Typist,其成员都是 int 等「值类型」或 string,因此我们在客户端程序 (Page_Load) 中执行「浅拷贝」时,既有的第一个「原型」对象 - dev 和 typist 实例,其所有的字段,会被逐位复制到新对象 devCopy1、devCopy2、typistCopy1 中,且正本对象和副本对象的字段各自独立、存储在内存的不同位置。
此外,我们看到 Developer 第一个「拷贝」出来的对象 devCopy1,它的 Role、PreferredLanguage 字段,都和原型对象 dev 一样,是「资深工程师」和「C#」,而我们可以将 Name 字段改成「李大同」,而不影响原型对象 dev 的 Name 字段。
图 2 Sybase PowerDesigner 12.5 的「反向工程」功能,已可解析 C# 3.0 的 Auto-Implemented Property 语法
" );
/* 执行结果:
王小明 - 资深工程师 - C#
李大同 - 资深工程师 - C#
吴宇泽 - 研发工程师 - C++
*/
Typist typist = new Typist(); // 原型对象(来自外部的第一个实例)
typist.Name = " 左婉青 " ;
typist.Role = " 行政文员 " ;
typist.WordsPerMinute = 120 ;
Typist typistCopy1 = (Typist)typist.Clone(); // 浅拷贝(shallow copy)
typistCopy1.Name = " 周玉婷 " ;
typistCopy1.WordsPerMinute = 115 ;
Response.Write(typist + "
" );
Response.Write(typistCopy1 + "
" );
/* 执行结果:
左婉青 - 行政文员 - 120 字/分
周玉婷 - 行政文员 - 115 字/分
*/
}
}
// 服务器端程序
namespace com.cnblogs.WizardWu.Sample01
{
// 员工 抽象类。
// 每个具体的「原型」类,要实现的抽象类或接口
abstract class Employee
{
// 返回 Employee 类型。
public abstract Employee Clone();
// .NET 3.0 的 Auto-Implemented Property (Automatic Properties) 语法,会自动产生对应的同名 private field
public string Name { get ; set ; } // 姓名
public string Role { get ; set ; } // 职务
}
// 程序员
// 继承 Employee 抽象类,或实现 .NET 的 ICloneable 接口,以便重写其 Clone 方法,创建作为当前实例副本的新对象
class Developer : Employee
{
public string PreferredLanguage { get ; set ; }
// 返回 Employee 类型。在客户端程序中,依赖和获得的是 Prototype 抽象类,并以其定义来操作其派生类
public override Employee Clone()
{
return (Employee)MemberwiseClone(); // 浅拷贝(shallow copy)。转型亦可用 as Employee
// return new Developer(); // 深拷贝(deep copy)
}
public override string ToString()
{
return string .Format( " {0} - {1} - {2} " , Name, Role, PreferredLanguage);
}
}
// 文员
// 继承 Employee 抽象类,或实现 .NET 的 ICloneable 接口,以便重写其 Clone 方法,创建作为当前实例副本的新对象
class Typist : Employee
{
public int WordsPerMinute { get ; set ; }
// 返回 Employee 类型。在客户端程序中,依赖和获得的是 Prototype 抽象类,并以其定义来操作其派生类
public override Employee Clone()
{
return (Employee)MemberwiseClone(); // 浅拷贝(shallow copy)。转型亦可用 as Employee
// return new Typist(); // 深拷贝(deep copy)
}
public override string ToString()
{
return string .Format( " {0} - {1} - {2} 字/分 " , Name, Role, WordsPerMinute);
}
}
} // end of namespace
图 3 上方示例 01_ShallowCopy.aspx.cs 的执行结果。在两个派生类的 Clone 方法中,用 MemberwiseClone 方法实现了「浅拷贝」
接下来我们要把上方的示例,在派生类 Developer 中,添加一个「引用类型」的数组 int[] intArray。由于数组是「引用数型」,因此在做「浅拷贝」时,原始对象及其复本引用的是同一个对象 (指向内存的同一个位置);当我们在 Page_Load 做完 Developer 的两次「浅拷贝」,再把最后一位程序员「吴宇泽」,他的 intArray 数组的第一个元素,其值从 1 改为 9,此时执行结果如下图 4 ,您会发现其他两位程序员 - 王小明、李大同,他们的 intArray 数组的第一个元素,也都同时被改成 9 了。
" );
Response.Write(dev.Display() + "
" );
Response.Write(devCopy1.Display() + "
" );
Response.Write(devCopy2.Display() + "
" );
// 只将最后一个程序员「吴宇泽」,他的数组(引用类型) 的第一个元素,其值从 1 改为 9
devCopy2.intArray[ 0 ] = 9 ;
Response.Write(dev.Display() + "
" );
Response.Write(devCopy1.Display() + "
" );
Response.Write(devCopy2.Display() + "
" );
/* 执行结果:
王小明 - 资深工程师 - C#
李大同 - 资深工程师 - C#
吴宇泽 - 研发工程师 - C++
1, 2, 3,
1, 2, 3,
1, 2, 3,
9, 2, 3,
9, 2, 3,
9, 2, 3,
*/
Typist typist = new Typist(); // 原型对象(来自外部的第一个实例)
typist.Name = " 左婉青 " ;
typist.Role = " 行政文员 " ;
typist.WordsPerMinute = 120 ;
Typist typistCopy1 = (Typist)typist.Clone(); // 浅拷贝(shallow copy)
typistCopy1.Name = " 周玉婷 " ;
typistCopy1.WordsPerMinute = 115 ;
Response.Write(typist + "
" );
Response.Write(typistCopy1 + "
" );
/* 执行结果:
左婉青 - 行政文员 - 120 字/分
周玉婷 - 行政文员 - 115 字/分
*/
}
}
// 服务器端程序
namespace com.cnblogs.WizardWu.Sample02
{
// 员工 抽象类。
// 每个具体的「原型」类,要实现的抽象类或接口
abstract class Employee
{
// 返回 Employee 类型。
public abstract Employee Clone();
// .NET 3.0 的 Auto-Implemented Property (Automatic Properties) 语法,会自动产生对应的同名 private field
public string Name { get ; set ; }
public string Role { get ; set ; }
}
// 程序员
// 继承 Employee 抽象类,或实现 .NET 的 ICloneable 接口,以便重写其 Clone 方法,创建作为当前实例副本的新对象
class Developer : Employee
{
public string PreferredLanguage { get ; set ; }
// 多加一个「引用类型(Reference Type)」成员 - 数组(Array)
public int [] intArray = { 1 , 2 , 3 };
public string Display()
{
string strReturn = string .Empty;
foreach ( int i in intArray)
{
strReturn += i.ToString() + " , " ;
}
return strReturn;
}
public override Employee Clone()
{
return (Employee)MemberwiseClone(); // 浅拷贝(shallow copy)。转型亦可用 as Employee
// return new Developer(); // 深拷贝(deep copy)
}
public override string ToString()
{
return string .Format( " {0} - {1} - {2} " , Name, Role, PreferredLanguage);
}
}
// 文员
// 继承 Employee 抽象类,或实现 .NET 的 ICloneable 接口,以便重写其 Clone 方法,创建作为当前实例副本的新对象
class Typist : Employee
{
public int WordsPerMinute { get ; set ; }
public override Employee Clone()
{
return (Employee)MemberwiseClone(); // 浅拷贝(shallow copy)。转型亦可用 as Employee
// return new Typist(); // 深拷贝(deep copy)
}
public override string ToString()
{
return string .Format( " {0} - {1} - {2} 字/分 " , Name, Role, WordsPerMinute);
}
}
} // end of namespace
图 4 Developer 类添加了一个「引用类型」的数组,造成「浅拷贝」时,只复制了「引用」,却未复制引用的字段
在下方的最后一个示例中,我们要更正前一个示例 02_ShallowCopy_fail.aspx.cs 的错误。我们把前述 Developer 类里面的 Clone 方法,从「浅拷贝」改成「深拷贝」,以搭配数组这个「引用类型」的拷贝;而另一个 Typist 类,由于没有「引用类型」成员,因此不需要更改,Clone 方法仍然延用「浅拷贝」。执行结果如下图 5,我们同样在 Page_Load 做 Developer 的两次拷贝,只不过这两次是「深拷贝」。结果符合我们需求,只有最后一位程序员「吴宇泽」,他的数组的第一个元素,其值从 1 改为 9,其他两位程序员则不受影响。
" );
Response.Write(dev.Display() + "
" );
Response.Write(devCopy1.Display() + "
" );
Response.Write(devCopy2.Display() + "
" );
// 只将最后一个程序员「吴宇泽」,他的数组(引用类型) 的第一个元素,其值从 1 改为 9
devCopy2.intArray[ 0 ] = 9 ;
Response.Write(dev.Display() + "
" );
Response.Write(devCopy1.Display() + "
" );
Response.Write(devCopy2.Display() + "
" );
/* 执行结果:
王小明 - 资深工程师 - C#
李大同 - 资深工程师 - C#
吴宇泽 - 研发工程师 - C++
1, 2, 3,
1, 2, 3,
1, 2, 3,
1, 2, 3,
1, 2, 3,
9, 2, 3,
*/
Typist typist = new Typist(); // 原型对象(来自外部的第一个实例)
typist.Name = " 左婉青 " ;
typist.Role = " 行政文员 " ;
typist.WordsPerMinute = 120 ;
Typist typistCopy1 = (Typist)typist.Clone(); // 浅拷贝(shallow copy)
typistCopy1.Name = " 周玉婷 " ;
typistCopy1.WordsPerMinute = 115 ;
Response.Write(typist + "
" );
Response.Write(typistCopy1 + "
" );
/* 执行结果:
左婉青 - 行政文员 - 120 字/分
周玉婷 - 行政文员 - 115 字/分
*/
}
}
// 服务器端程序
namespace com.cnblogs.WizardWu.Sample03
{
// 员工 抽象类。
// 每个具体的「原型」类,要实现的抽象类或接口
abstract class Employee
{
// 返回 Employee 类型。
public abstract Employee Clone();
// .NET 3.0 的 Auto-Implemented Property (Automatic Properties) 语法,会自动产生对应的同名 private field
public string Name { get ; set ; }
public string Role { get ; set ; }
}
// 程序员
// 继承 Employee 抽象类,或实现 .NET 的 ICloneable 接口,以便重写其 Clone 方法,创建作为当前实例副本的新对象
class Developer : Employee
{
public string PreferredLanguage { get ; set ; }
// 多加一个「引用类型(Reference Type)」成员 - 数组(Array)
public int [] intArray = { 1 , 2 , 3 };
public string Display()
{
string strReturn = string .Empty;
foreach ( int i in intArray)
{
strReturn += i.ToString() + " , " ;
}
return strReturn;
}
public override Employee Clone()
{
// return (Employee)MemberwiseClone(); // 浅拷贝(shallow copy)。转型亦可用 as Employee
// return new Developer(); // 深拷贝(deep copy) - 做法一
// 深拷贝(deep copy) - 做法二
Developer dev1 = new Developer(); // 用 new 创建此类的实例
dev1.Name = this .Name;
dev1.Role = this .Role;
dev1.PreferredLanguage = this .PreferredLanguage;
return dev1;
}
public override string ToString()
{
return string .Format( " {0} - {1} - {2} " , Name, Role, PreferredLanguage);
}
}
// 文员
// 继承 Employee 抽象类,或实现 .NET 的 ICloneable 接口,以便重写其 Clone 方法,创建作为当前实例副本的新对象
class Typist : Employee
{
public int WordsPerMinute { get ; set ; }
public override Employee Clone()
{
return (Employee)MemberwiseClone(); // 浅拷贝(shallow copy)。转型亦可用 as Employee
// return new Typist(); // 深拷贝(deep copy)
}
public override string ToString()
{
return string .Format( " {0} - {1} - {2} 字/分 " , Name, Role, WordsPerMinute);
}
}
} // end of namespace
图 5 将 Developer 类中 Clone 方法里的「浅拷贝」改成「深拷贝」,以配合该类中「引用类型」成员的复制
--------------------------------------------------------
Prototype Pattern 适用的情景:
- 当系统中,某个系列的类,变化和扩展特别频繁的时侯。
- 当系统应该独立于它的产品创建时。
- 想对客户端程序,隐藏类的具体内容。
- 希望依用户的操作,在「执行时期」动态地加载 (dynamic loading) 或动态获得对象的状态。例如本帖一开始提到的室内设计软件,会依使用者鼠标的操作来动态创建副本。
- 同上一点,当需要的类型不是编译时就能确定的,而是能在运行过程中动态选择的。
- 当类型本身可枚举的种类非常固定时,例如一家软件公司,只有「主管、程序员、业务员」三种职务,当公司标到大型项目,需要招幕一百个程序员,与其通过某种机制 new 一百个程序员实例,不如通过一个现成的「程序员」原型实例,克隆一百个对象出来。
- 当一个类的多个实例,他们之间的字段和属性只有些许不同时。
- 当一个类的实例,只能有几种不同状态组合的其中一种时。
- 当对象的初始化需要高成本,例如:构造函数的参数、字段数量很多很复杂时。
Prototype Pattern 的优点:
- 可达到资源优化,避免用 new 创建实例会较消耗资源,且这样做速度也比 clone 对象慢 [15], [17], [18]。
- 独立性和灵活性高,容易动态加载新功能。
- 减少类的数量。避免类的种类太多时,其子类数量会迅速地增加。
- 能在客户端程序中隐藏对象创建的细节。
Prototype Pattern 的缺点:
- 每一个类都需要配备并覆写 Clone 方法,且撰写时需要做整个系统架构的通盘考量。
Prototype Pattern 的其他特性:
- 可避免形成多个类与类之间的大量平行 (平级) 阶层结构,在宽度和深度上的扩展。
- 「浅拷贝」可以提供低成本的对象复制;但通过「序列化 (Serialization)」进行的「深拷贝」代价就比较大,而非低成本的。
--------------------------------------------------------
本帖的最后,提供一位 Java 大师 - 结城浩,所绘制的 Prototype Pattern 趣味四格漫画,原地址如下:
Giko 猫谈 DP 四格漫画:
http://www.javaworld.com.tw/jute/post/view?bid=44&id=40932&sty=3&age=0&tpg=1&ppg=1#40932
http://www.hyuki.com/dp/cat_Prototype.html
∧_∧ 敲敲敲 ╱
( ) ∧ ∧ < 等于是利用 copy & paste 来制作实例..恩....。
( ) (,,゚Д゚) ╲____________
______ (つ_つ____
| 日∇ ╲|ThinkPad|╲
| ========= ╲
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
____________
╱
| 喔~、是 Prototype Pattern 吗?
╲ __ __________
|╱
∧_∧ ╱
( ・∀・) ∧ ∧ < 你你是谁? ...有..有什么事嬷你?...
( ⊃ ) (゚Д゚;) ╲____________
________(つ_つ____
| 日∇ ╲|ThinkPad|╲
| ========= ╲
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
__________
╱
| 利用实例来创建实例..恩
╲ __ ________
|╱
∧_∧ ╱
( ・∀・) ∧ ∧ < 恩..可以那样说...类变成配角.
( ) (;゚Д゚) ╲____________
_____ (つ_つ____
| 日∇╲|ThinkPad|╲
| ========= ╲
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
__________
╱
| 把类的设计失败, 在实例去修正.恩.
╲ __ ________
|╱
∧_∧ ╱
( ・∀・) ∧ ∧ < 不..不是这样啦..
( ⊃ ) (゚Д゚;) ╲____________
_____(つ_つ____
| 日∇ ╲|ThinkPad|╲
| ========= ╲
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
giko猫.clone()
∧ ∧ ┌────────────────
( ゚Д゚ ) < giko.clone()
U U └────────────────
| |
U U
∧ ∧ ∧ ┌────────────────
( ゚Д ゚Д゚ ) < 这这是、
U U U .└────────────────
| |
U U U
∧ ∧∧∧ ┌────────────────
( ゚Д゚ ゚Д゚ ) < 到底到底是、
U U.U U └────────────────
| |
.U UU U
∧ ∧ ∧ ∧ ┌────────────────
( ゚Д゚ >< ゚Д゚ ) < 是 shallow copy 是 shallow copy 吗
U U U U └────────────────
| >< |
.U U U U
. ∧ ∧ ∧ ∧ ┌────────────────
( ゚Д゚ ) * ( ゚Д゚ ) < 或是 deep copy。或是 deep copy。
U U U U └────────────────
| .| .* | |
U U .U U
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
--------------------------------------------------------
本帖的内容和四个示例,已过滤掉 Prototype Pattern 非必要的功能和角色,主要目的是让初学者能够快速入门。实务上 Prototype Pattern 还能有很多变化和进阶应用,比如:
- 整合「工厂模式」,用一个工厂类来单独负责构造的工作 [8], [19]。
- 通过「序列化 / 反序列化」实现深拷贝 [2], [13], [14], [20]。
- 客户端程序多通过一个 Prototype manager 去创建 ConcretePrototype 对象,避免客户端程序与 ConcretePrototype 类产生依赖 [4], [5], [14], [15], [20], [21]。
有兴趣深入研究的网友,可参阅下方的「相关文章」和「相关书籍」。
--------------------------------------------------------
相关文章:
[1] .NET深入学习笔记(4) 深拷贝与浅拷贝(Deep Copy and Shallow Copy) - 老徐的博客 - 博客园
http://www.cnblogs.com/frank_xl/archive/2009/02/24/1396903.html
[2] C# Tips 浅拷贝和深拷贝 - SQL SERVER - 数据库-数据仓库 - ZDNetChina中文社区
http://bbs.zdnet.com.cn/thread-1426176-1-1.html
[3] 小议 .NET 中的对象拷贝 - TerryLee's Tech Space - 博客园
http://terrylee.cnblogs.com/archive/2006/01/06/312493.html
(推荐此帖)
[4] C#设计模式(9)-Prototype Pattern - First we try, then we trust - 博客园
http://www.cnblogs.com/zhenyulu/articles/39257.html
[5] Prototype Design Pattern in C# and VB.NET (英文)
http://www.dofactory.com/Patterns/PatternPrototype.aspx
[6] Proxy 模式,作者: caterpillar (繁体中文)
http://caterpillar.onlyfun.net/Gossip/DesignPattern/PrototypePattern.htm
http://www.javaworld.com.tw/jute/post/view?bid=44&id=25500&sty=1&tpg=3&age=-1
[7] 原型模式(ProtoType) - 最简单的 - JavaEye技术网站
http://iwtxokhtd.javaeye.com/blog/361086
http://www.codeweblog.com/prototype-model-prototype/
[8] 设计模式学习笔记五——Prototype模式 - 每天进步一点点,微笑面对全世界! - JavaEye技术网站
http://mybluesky99.javaeye.com/blog/384252
[9] Prototype Design Pattern :: BlackWasp Software Development (英文)
http://www.blackwasp.co.uk/Prototype.aspx
[10] 设计模式学习笔记(六)——Prototype原型模式 - KiddLee - 博客园
http://www.cnblogs.com/kid-li/archive/2006/05/18/403559.html
[11] Prototype pattern - Wikipedia, the free encyclopedia (英文)
http://en.wikipedia.org/wiki/Prototype_pattern
[12] DotNet Framework源代码中的模式(六)——Prototype(原型模式) - Guushuuse _NET - 博客园
http://www.cnblogs.com/guushuuse/archive/2009/05/15/1457951.html
[13] 无废话C#设计模式之五:Prototype - LoveCherry - 博客园
http://www.cnblogs.com/lovecherry/archive/2007/10/06/915535.html
[14] .NET设计模式(6):原型模式(Prototype Pattern) - TerryLee's Tech Space - 博客园
http://www.cnblogs.com/Terrylee/archive/2006/01/16/317896.html
[15] ASP_NET Wiki Architecture Design Patterns (英文)
http://wiki.asp.net/page.aspx/499/prototype-pattern/
http://wiki.asp.net/page.aspx/276/design-patterns/
[16] Implementing Audit / history tracking using Prototype Pattern (英文)
http://www.codeproject.com/KB/aspnet/AuditTracking.aspx