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