一、原型模式的概念 原型模式屬於創建型設計模式。當要創建的對象類型由原型實例確定時使用它,該實例被克隆以生成新對象。 此模式用於 1、避免客戶端應用程式中的對象創建者的子類,如工廠方法模式。 2、避免以標準方式創建新對象的固有成本(例如,使用'new'關鍵字),當它對於給定的應用程式來說過於昂貴時。 ...
一、原型模式的概念
原型模式屬於創建型設計模式。當要創建的對象類型由原型實例確定時使用它,該實例被克隆以生成新對象。
此模式用於
1、避免客戶端應用程式中的對象創建者的子類,如工廠方法模式。
2、避免以標準方式創建新對象的固有成本(例如,使用'new'關鍵字),當它對於給定的應用程式來說過於昂貴時。
上述定義來自wiki,簡單來說,就是創建一個全新的對象代價太高時,比如耗時太長時,能直接從現有實例中以較小的代價克隆出一個對象。
二、原型模式的實現
實現原型模式的方式之一為克隆,下麵我們來實現一下
1、先定義一個抽象原型,包含兩個克隆的虛方法(方便子類重寫)
namespace PrototypePattern
{
using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
[Serializable]
public abstract class AbstractPrototype
{
/// <summary>
/// 淺克隆
/// <remarks>使用objectMemberwiseClone</remarks>
/// </summary>
/// <returns>當前對象副本</returns>
public virtual AbstractPrototype Clone()
{
return (AbstractPrototype)this.MemberwiseClone();
}
/// <summary>
/// 深克隆
/// <remarks>使用二進位序列化反序列化實現</remarks>
/// </summary>
/// <returns>當前對象副本</returns>
public virtual AbstractPrototype DeepClone()
{
MemoryStream stream = new MemoryStream();
BinaryFormatter binaryFormatter = new BinaryFormatter();
binaryFormatter.Serialize(stream, this);
stream.Position = 0;
var instance = (AbstractPrototype)binaryFormatter.Deserialize(stream);
stream.DisposeAsync();
return instance;
}
}
}
2、需要實現原型模式的類繼承此抽象類即可
namespace PrototypePattern
{
using System;
[Serializable]
public class ConcretePrototype : AbstractPrototype
{
public int Number { get; set; }
public Person Person { get; set; }
}
}
Person類
namespace PrototypePattern
{
using System;
[Serializable]
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
}
以上代碼就實現了原型模式。
其中的克隆方式又分為淺克隆,深克隆,深克隆使用的是序列化反序列化的方式(需要在類上增加[Serializable]特性)。
淺克隆
複製出來的對象的所有變數都含有與原來的對象相同的值,而所有的對其他對象的引用仍然指向原來的對象。
深克隆
複製出來的所有變數都含有與原來的對象相同的值,那些引用其他對象的變數將指向複製出來的新對象,是一個全新的副本。
我們來測試一波
{
ConcretePrototype no0 = new ConcretePrototype() { Number = 0, Person = new Person() { Age = 17, Name = "Vincent" } };
Console.WriteLine("第一次構造");
Console.WriteLine($"No0:Number:{no0.Number},Age:{no0.Person.Age},Name:{no0.Person.Name}");
Console.WriteLine("從No0淺克隆到No1");
ConcretePrototype no1 = (ConcretePrototype)no0.Clone();
Console.WriteLine("修改No1");
no1.Person.Age = 18;
no1.Person.Name = "Vincent1";
Console.WriteLine($"No0:Number:{no0.Number},Age:{no0.Person.Age},Name:{no0.Person.Name}");
Console.WriteLine($"No1:Number:{no1.Number},Age:{no1.Person.Age},Name:{no1.Person.Name}");
Console.WriteLine("******************");
Console.WriteLine("從No0淺克隆到No2");
ConcretePrototype no2 = (ConcretePrototype)no0.DeepClone();
Console.WriteLine("修改No2");
no2.Person.Age = 19;
no2.Person.Name = "Vincent2";
Console.WriteLine($"No0:Number:{no0.Number},Age:{no0.Person.Age},Name:{no0.Person.Name}");
Console.WriteLine($"No2:Number:{no2.Number},Age:{no2.Person.Age},Name:{no2.Person.Name}");
Console.WriteLine("******************");
}
看看結果
可以看到,通過淺克隆得到的對象和原對象共用引用對象,深克隆的反之。
以上是一般的實現方式,但是這裡使用的是繼承抽象原型類的方式。我們知道,C#里是單繼承,我們這裡繼承了抽象的原型類,就不能繼承其他類了,難免會有些不方便。下麵我們來改進一下。
三、改進
我們知道.NET框架里為我們提供了ICloneable介面,此介面只包含一個返回值為object的Clone方法。object類中提供了返回值為object、訪問級別為protected的MemberwiseClone方法。那麼我們只需要繼承ICloneable介面,實現Clone方法,把Clone方法訪問級別改為public即可實現原型模式,如下
namespace PrototypePattern { using System; [Serializable] public class ConcretePrototype2 : ICloneable { public int Number { get; set; } public Person Person { get; set; } /// <summary>Creates a new object that is a copy of the current instance.</summary> /// <returns>A new object that is a copy of this instance.</returns> public object Clone() { return this.MemberwiseClone(); } } }
這裡只有淺克隆,沒有深克隆,怎麼辦?我們使用擴展方法,為ICloneable介面擴展一個深克隆方法,如下
namespace PrototypePattern { using System; using System.IO; using System.Runtime.Serialization.Formatters.Binary; public static class ICloneableExtension { public static T DeepClone<T>(this ICloneable instance) { MemoryStream stream = new MemoryStream(); BinaryFormatter binaryFormatter = new BinaryFormatter(); binaryFormatter.Serialize(stream, instance); stream.Position = 0; var duplicate = binaryFormatter.Deserialize(stream); stream.DisposeAsync(); return (T)duplicate; } } }
四、總結
以上的實現只能應對一般的情況,如果需要應對複雜的情況,如克隆對象中指定的對象(例如單據的複製),就需要改進或增加克隆的實現方式。
文中的代碼下載:https://github.com/hzhhhbb/PrototypePattern
五、參考資料
https://en.wikipedia.org/wiki/Prototype_pattern