结构图
角色
l 客户(Client)角色:客户类提出创建对象的请求。
l 抽象原型(Prototype)角色:这是一个抽象角色,通常由一个C#接口或抽象类实现。此角色给出所有的具体原型类所需的接口。在C#中,抽象原型角色通常事项了ICloneable接口。
l 具体原型(Concrete Prototype)角色:被复制的对象。此角色需要事项抽象原型角色所要求的接口。
l 原型管理器(Prototype Manager)角色:创建具体原型类的对象,并记录每一个被创建的对象。
意图
用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
适用性
l 要实例化的类是在运行时刻指定时,例如,通过动态装载;
l 或者为了避免创建一个与产品类层次平行的工厂类层次时;
l 或者当一个类的实例只能有几个不同状态组合中的一种时。建立相应数目的原型并克隆它们可能比每次用合适的状态手工实例化该类更方便一些。
浅拷贝和深拷贝之间的区别:
l 浅拷贝(Shallow Copy)复制出一个和原对象相同类型的新实例,它包含原对象所有值类型成员的复本。对于引用类型字段,仅拷贝它的引用到目标对象。如果改变目标对象中引用型字段的值他将反映在原对象中,也就是说原始对象中对应的字段也会发生变化。
l 深拷贝(Deep Copy)复制出一个新实例,它包含所有和原对象中对应字段相同(内容相同)的字段,也就是说这个引用和原是对象的引用是不同的,我们在改变新对象中的这个字段的时候是不会影响到原始对象中对应字段的内容参考。
对比浅拷贝与深拷贝
Imports System
Imports System.IO
Imports System.Runtime.Serialization.Formatters.Binary
'MainApp test application
Public Class MainAppClass MainApp
Public Shared Sub Main()Sub Main()
Dim shallowFrom As New CloneSample
Dim shallowTo As CloneSample = shallowFrom.ShallowClone()
shallowFrom.numbers(0) = 9
Console.WriteLine("The result--")
shallowFrom.Display()
shallowTo.Display()
Console.WriteLine("The result--")
Dim deepFrom As New CloneSample
Dim deepTo As CloneSample = shallowFrom.ShallowClone()
shallowFrom.numbers(0) = 9
deepFrom.Display()
deepTo.Display()
End Sub
End Class
Public Class CloneSampleClass CloneSample
Public numbers As Integer() = New Integer(){1,2,3}
Public Function ShallowClone()Function ShallowClone() As CloneSample
Return TryCast(Me.MemberwiseClone(),CloneSample)
End Function
Public Function DeepClone()Function DeepClone() As CloneSample
Dim memoryStream As New MemoryStream()
Dim formatter As New BinaryFormatter()
formatter.Serialize(memoryStream, Me)
memoryStream.Position = 0
Return TryCast(formatter.Deserialize(memoryStream), CloneSample)
End Function
Public Sub Disaplay()Sub Disaplay()
For Each number As Integer In numbers
Console.Write(number & ",")
Next
Console.WriteLine()
End Sub
End Class
运行结果
The Result--
9,2,3,
9,2,3,
The Result--
1,2,3,
9,2,3,
示意性代码
浅拷贝结构代码
'Prototype pattern -- Structural example
Imports System
'MainApp test application
Public Class MainAppClass MainApp
Public Shared Sub Main()Sub Main()
'Create two instances and clone each
Dim p1 As New ConcretePrototype1("I")
Dim c1 As ConcretePrototype1 = DirectCAst(p1.Clone(),ConcretePrototype1)
Console.WriteLine("Cloned: {0}", c1.Id)
Dim p2 As New ConcretePrototype2("II")
Dim c2 As ConcretePrototype2 = DirectCAst(p2.Clone(),ConcretePrototype2)
Console.WriteLine("Cloned: {0}", c2.Id)
'Wait for user
Console.ReadLine()
End Sub
End Class
'"Prototype"
Public mustinherit Class PrototypeClass Prototype
private _id As String
'Constructor
Public Sub New()Sub New(ByVal id As String)
me._id = id
End Sub
'Property
Public ReadOnly Property Id()property Id As String
Get
return _id
End Get
End property
Public mustoverride Function Clone()Function Clone() As Prototype
End Class
'"ConcretePrototype1"
Public Class ConcretePrototype1Class ConcretePrototype1
Inherits Prototype
'Constructor
Public Sub New()Sub New(ByVal id As String)
mybAse.new(id)
End Sub
Public Overrides Function Clone()Function Clone() As Prototype
'Shallow copy
return TryCAst(me.MemberwiseClone(),Prototype)
End Function
End Class
'"ConcretePrototype2"
Public Class ConcretePrototype2Class ConcretePrototype2
Inherits Prototype
'Constructor
Public Sub New()Sub New(ByVal id As String)
mybAse.new(id)
End Sub
Public Overrides Function Clone()Function Clone() As Prototype
'Shallow copy
return TryCAst(me.MemberwiseClone(),Prototype)
End Function
End Class
带Prototype Manager的原型模式
结构图
角色
l 客户(Client)角色:客户端类向原型管理器提出创建对象的请求。
l 抽象原型(Prototype)角色:这是一个抽象角色,通常由一个C#接口或抽象类实现。此角色给出所有的具体原型类所需的接口。在C#中,抽象原型角色通常实现了ICloneable接口。
l 具体原型(Concrete Prototype)角色:被复制的对象。此角色需要实现抽象的原型角色所要求的接口。
l 原型管理器(Prototype Manager)角色:创建具体原型类的对象,并记录每一个被创建的对象。
示意性结构代码
带Manager的示意性代码
'Prototype pattern -- Structural example
Imports System
Imports System.Collections
'MainApp test application
Public Class MainAppClass MainApp
Public Shared Sub Main()Sub Main()
'Add two instances into prototype manager.
Dim manager As New PrototypeManager()
manager("one") = New ConcretePrototype("I")
manager("two") = New ConcretePrototype("II")
'Clone the two instance.
Dim name As String = "one"
Dim c1 As Prototype = manager(name).Clone()
Console.WriteLine("Cloned: {0}", c1.Id)
name = "two"
Dim c2 As Prototype = manager(name).Clone()
Console.WriteLine("Cloned: {0}", c2.Id)
'Wait for user
Console.ReadLine()
End Sub
End Class
' Prototype manager
Public Class PrototypeManagerClass PrototypeManager
Private prototypes As New HashTable()
' Indexer
Public Default Property Item()Property Item(ByVal name As String) As ConcretePrototype
Get
Return TryCast(prototypes(name), ConcretePrototype)
End Get
Set
prototypes.Add(name, value)
End Set
End Property
End Class
'"Prototype"
Public MustInherit Class PrototypeClass Prototype
Private me_id As String
'Constructor
Public Sub New()Sub New(ByVal id As String)
Me.me_id = id
End Sub
'Property
Public ReadOnly Property Id()property Id As String
Get
Return me_id
End Get
End property
Public mustoverride Function Clone()Function Clone() As Prototype
End Class
'"ConcretePrototype"
Public Class ConcretePrototypeClass ConcretePrototype
Inherits Prototype
'Constructor
Public Sub New()Sub New(ByVal id As String)
Mybase.New(id)
End Sub
Public Overrides Function Clone()Function Clone() As Prototype
'Shallow copy
Return TryCast(me.MemberwiseClone(),Prototype)
End Function
End Class
实例
下面这个例子演示了在原型管理器中存储用户预先定义的颜色原型,客户通过原型管理器克隆颜色对象。
实例
' Prototype pattern -- Real World example
Imports System
Imports System.Collections
Namespace DoFactoryNamespace DoFactory.GangOfFour.Prototype.RealWorld
'=========================================================================================
'客户程序
'=========================================================================================
' MainApp test application
Class MainAppClass MainApp
Private Shared Sub Main()Sub Main()
Dim colormanager As New ColorManager()
' Initialize with standard colors
colormanager("red") = New Color(255, 0, 0)
colormanager("green") = New Color(0, 255, 0)
colormanager("blue") = New Color(0, 0, 255)
' User adds personalized colors
colormanager("angry") = New Color(255, 54, 0)
colormanager("peace") = New Color(128, 211, 128)
colormanager("flame") = New Color(211, 34, 20)
Dim color As Color
' User uses selected colors
Dim name As String = "red"
color = TryCast(colormanager(name).Clone(), Color)
name = "peace"
color = TryCast(colormanager(name).Clone(), Color)
name = "flame"
color = TryCast(colormanager(name).Clone(), Color)
' Wait for user
Console.Read()
End Sub
End Class
'=========================================================================================
'稳定的部分
'=========================================================================================
' "Prototype"
MustInherit Class ColorPrototypeClass ColorPrototype
Public MustOverride Function Clone()Function Clone() As ColorPrototype
End Class
' Prototype manager
Class ColorManagerClass ColorManager
Private colors As New Hashtable()
' Indexer
Public Default Property Item()Property Item(ByVal name As String) As ColorPrototype
Get
Return TryCast(colors(name), ColorPrototype)
End Get
Set
colors.Add(name, value)
End Set
End Property
End Class
'=========================================================================================
'容易变化的部分
'=========================================================================================
' "ConcretePrototype"
Class ColorClass Color
Inherits ColorPrototype
Private red As Integer
Private green As Integer
Private blue As Integer
' Constructor
Public Sub New()Sub New(ByVal red As Integer, ByVal green As Integer, ByVal blue As Integer)
Me.red = red
Me.green = green
Me.blue = blue
End Sub
' Create a shallow copy
Public Overloads Overrides Function Clone()Function Clone() As ColorPrototype
Console.WriteLine("Cloning color RGB: {0,3},{1,3},{2,3}", red, green, blue)
Return TryCast(Me.MemberwiseClone(), ColorPrototype)
End Function
End Class
End Namespace
Prototype模式的几个要点:
1、Prototype模式同样用于隔离类对象的使用者和具体类型(易变类)之间的耦合关系,它同样要求这些“易变类”拥有“稳定的接口”。
2、Prototype模式对于“如何创建易变类的实体对象”采用“原型克隆”的方法来做,它使得我们可以非常灵活地动态创建“拥有某些稳定接口”的新对象,所需工作仅仅是注册一个新类的对象(即原型),然后在任何需要的地方不断地Clone。
3、Prototype模式中的Clone方法可以利用.NET中的Object类的MemberwiseClone()方法(浅拷贝)或者序列化(深拷贝)来实现。
参考资料:
《小议.NET中的对象拷贝》 TerryLee
《原型模式(Prototype Pattern)》 小山
《C#面向对象设计模式纵横谈系列课程(6)》 李建中老师