前言
嗨,早上好!
想象一下你正在制作蛋糕,如果每次都要從頭開始準(zhǔn)備原材料并烘焙,那將會非常耗時。
但如果已經(jīng)有了一個現(xiàn)成的蛋糕作為模板,只需要復(fù)制它并根據(jù)需要做些小改動,就能節(jié)省大量時間。
原型模式就像這個過程,讓我們可以快速地創(chuàng)建對象副本,同時保持靈活性和效率。
在 C# 中,實現(xiàn)原型模式非常輕松,來看看有哪些方式吧!
基本結(jié)構(gòu)
ConcretePrototype 類:實現(xiàn) Prototype 接口的具體類。
Client 類:使用 Prototype 接口來克隆具體對象。
傳統(tǒng)實現(xiàn)方式
// 1. 定義 Prototype 接口
public abstract class NormalActor
{
public abstract NormalActor clone();
}
// 2. 實現(xiàn) Prototype 接口的具體類
public class NormalActorA:NormalActor
{
public override NormalActor clone()
{
Console.WriteLine("NormalActorA is call");
return (NormalActor)this.MemberwiseClone();
}
}
// 2. 實現(xiàn) Prototype 接口的具體類
public class NormalActorB :NormalActor
{
public override NormalActor clone()
{
Console.WriteLine("NormalActorB was called");
return (NormalActor)this.MemberwiseClone();
}
}
// 3. Client 使用
public class GameSystem
{
public void Run(NormalActor normalActor)
{
NormalActor normalActor1 = normalActor.clone();
NormalActor normalActor2 = normalActor.clone();
NormalActor normalActor3 = normalActor.clone();
NormalActor normalActor4 = normalActor.clone();
NormalActor normalActor5 = normalActor.clone();
}
}
GameSystem gameSystem = new GameSystem();
gameSystem.Run(new NormalActorA());
實現(xiàn) ICloneable 接口方式
傳統(tǒng)實現(xiàn)方式需要自己定義 Prototype 接口,實際上,C# 已經(jīng)幫我們定義了 Prototype 接口了,就是 ICloneable 接口,直接實現(xiàn)它就可以了:
// 1. 實現(xiàn) Prototype 接口的具體類
public class Person : ICloneable
{
public string Name { get; set; }
public int Age { get; set; }
public Address Address { get; set; } // 引用類型字段
// 實現(xiàn)ICloneable接口的Clone方法
public object Clone()
{
return this.MemberwiseClone(); // 使用Object的MemberwiseClone方法實現(xiàn)淺拷貝
}
public void Display()
{
Console.WriteLine($"Name: {Name}, Age: {Age}, Address: {Address.Street}, {Address.City}");
}
}
public class Address
{
public string Street { get; set; }
public string City { get; set; }
}
// 2. Client 使用
var originalPerson = new Person
{
Name = "John Doe",
Age = 30,
Address = new Address { Street = "123 Main St", City = "New York" }
};
var clonedPerson = (Person)originalPerson.Clone();
// 修改克隆對象的屬性
clonedPerson.Name = "Jane Smith";
clonedPerson.Age = 25;
clonedPerson.Address.Street = "456 Oak Ave"; // 這會同時修改原始對象的Address
originalPerson.Display(); // 輸出: Name: John Doe, Age: 30, Address: 456 Oak Ave, New York
clonedPerson.Display(); // 輸出: Name: Jane Smith, Age: 25, Address: 456 Oak Ave, New York
深拷貝實現(xiàn)
以上的實現(xiàn)方式非常簡單,但有一個問題,就是實現(xiàn)的是淺拷貝,只能復(fù)制對象本身以及其中的值類型字段,對于引用類型字段,就只復(fù)制引用而不復(fù)制引用的對象,這樣一旦引用類型字段被修改了,就會影響到其它地方的使用,這在上面的例子中也可以感受到,所以需要創(chuàng)建一個完全獨立的副本,即深拷貝實現(xiàn)。
深拷貝的實現(xiàn)主要有兩種方法:
手動復(fù)制所有字段(雖然比較笨,但對象字段比較少且比較固定時,也不失為一個好方法)
using System;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
[Serializable] // 需要標(biāo)記為可序列化
public class Person : ICloneable
{
public string Name { get; set; }
public int Age { get; set; }
public Address Address { get; set; }
// 深拷貝實現(xiàn)
public object Clone()
{
// 使用序列化和反序列化實現(xiàn)深拷貝
using (var memoryStream = new MemoryStream())
{
var formatter = new BinaryFormatter();
formatter.Serialize(memoryStream, this);
memoryStream.Position = 0;
return formatter.Deserialize(memoryStream);
}
}
// 另一種深拷貝實現(xiàn)方式(手動復(fù)制所有字段)
// public Person DeepCopy()
// {
// var copy = (Person)this.MemberwiseClone();
// copy.Address = new Address
// {
// Street = this.Address.Street,
// City = this.Address.City
// };
// return copy;
// }
public void Display()
{
Console.WriteLine($"Name: {Name}, Age: {Age}, Address: {Address.Street}, {Address.City}");
}
}
[Serializable]
public class Address
{
public string Street { get; set; }
public string City { get; set; }
}
// 使用示例
var originalPerson = new Person
{
Name = "John Doe",
Age = 30,
Address = new Address { Street = "123 Main St", City = "New York" }
};
// 使用序列化方式的深拷貝
var clonedPerson = (Person)originalPerson.Clone();
// 或者使用手動實現(xiàn)的深拷貝
// var clonedPerson = originalPerson.DeepCopy();
// 修改克隆對象的屬性
clonedPerson.Name = "Jane Smith";
clonedPerson.Age = 25;
clonedPerson.Address.Street = "456 Oak Ave"; // 不會影響原始對象
originalPerson.Display(); // 輸出: Name: John Doe, Age: 30, Address: 123 Main St, New York
clonedPerson.Display(); // 輸出: Name: Jane Smith, Age: 25, Address: 456 Oak Ave, New York
總結(jié)
相比于每次都創(chuàng)建新對象,利用原型模式復(fù)制現(xiàn)有對象通常更快。
在文檔編輯、緩存系統(tǒng)、配置管理和圖形用戶界面(GUI)開發(fā)等業(yè)務(wù)場景,原型模式都能發(fā)揮很大作用!
閱讀原文:
該文章在 2025/5/9 12:11:08 編輯過