前言
嗨,早上好!
想象一下你正在制作蛋糕,如果每次都要从头开始准备原材料并烘焙,那将会非常耗时。
但如果已经有了一个现成的蛋糕作为模板,只需要复制它并根据需要做些小改动,就能节省大量时间。
原型模式就像这个过程,让我们可以快速地创建对象副本,同时保持灵活性和效率。
在 C# 中,实现原型模式非常轻松,来看看有哪些方式吧!
基本结构
ConcretePrototype 类:实现 Prototype 接口的具体类。
Client 类:使用 Prototype 接口来克隆具体对象。
传统实现方式
// 1. 定义 Prototype 接口
public abstract class NormalActor
{
public abstract NormalActor clone();
}
// 2. 实现 Prototype 接口的具体类
public class NormalActorA:NormalActor
{
public override NormalActor clone()
{
Console.WriteLine("NormalActorA is call");
return (NormalActor)this.MemberwiseClone();
}
}
// 2. 实现 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());
实现 ICloneable 接口方式
传统实现方式需要自己定义 Prototype 接口,实际上,C# 已经帮我们定义了 Prototype 接口了,就是 ICloneable 接口,直接实现它就可以了:
// 1. 实现 Prototype 接口的具体类
public class Person : ICloneable
{
public string Name { get; set; }
public int Age { get; set; }
public Address Address { get; set; } // 引用类型字段
// 实现ICloneable接口的Clone方法
public object Clone()
{
return this.MemberwiseClone(); // 使用Object的MemberwiseClone方法实现浅拷贝
}
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
深拷贝实现
以上的实现方式非常简单,但有一个问题,就是实现的是浅拷贝,只能复制对象本身以及其中的值类型字段,对于引用类型字段,就只复制引用而不复制引用的对象,这样一旦引用类型字段被修改了,就会影响到其它地方的使用,这在上面的例子中也可以感受到,所以需要创建一个完全独立的副本,即深拷贝实现。
深拷贝的实现主要有两种方法:
手动复制所有字段(虽然比较笨,但对象字段比较少且比较固定时,也不失为一个好方法)
using System;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
[Serializable] // 需要标记为可序列化
public class Person : ICloneable
{
public string Name { get; set; }
public int Age { get; set; }
public Address Address { get; set; }
// 深拷贝实现
public object Clone()
{
// 使用序列化和反序列化实现深拷贝
using (var memoryStream = new MemoryStream())
{
var formatter = new BinaryFormatter();
formatter.Serialize(memoryStream, this);
memoryStream.Position = 0;
return formatter.Deserialize(memoryStream);
}
}
// 另一种深拷贝实现方式(手动复制所有字段)
// 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();
// 或者使用手动实现的深拷贝
// 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
总结
相比于每次都创建新对象,利用原型模式复制现有对象通常更快。
在文档编辑、缓存系统、配置管理和图形用户界面(GUI)开发等业务场景,原型模式都能发挥很大作用!
阅读原文:
该文章在 2025/5/9 12:11:08 编辑过