原型模式,用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象
原型模式其实就是从一个对象再创建另外一个可定制的对象而且不需要知道任何创建的细节。
实现方式#
实现方式大致如下:
在 c# 中可以借助 ICloneable
接口和 MemberwiseClone
方法来方便的实现原型模式,需要注意的是这个方法是浅复制,当对象内嵌套了另外一个对象的时候,尤其需要注意。
// 浅复制
public object Clone() => MemberwiseClone();
示例#
简单的简历,所有的字段属性都是简单类型
public class SimpleResume : ICloneable
{
private string _name;
private string _email;
private string _timePeriod;
private string _company;
public void SetPersonalInfo(string name, string email)
{
_name = name;
_email = email;
}
public void SetWorkExperience(string company, string timePeriod)
{
_company = company;
_timePeriod = timePeriod;
}
public void Display()
{
Console.WriteLine($"{_name} {_email}");
Console.WriteLine($"工作经历:{_timePeriod} {_company}");
}
public object Clone() => MemberwiseClone();
}
var resume = new SimpleResume();
resume.SetPersonalInfo("小明", "xiaoming@abc.xyz");
resume.SetWorkExperience("xxx公司", "1990~2000");
resume.Display();
var resume1 = (SimpleResume)resume.Clone();
resume1.SetWorkExperience("xxx企业", "1998~1999");
resume1.Display();
var resume2 = (SimpleResume)resume.Clone();
resume2.SetPersonalInfo("xiaohong", "xiaohong@abc.xyz");
resume2.Display();
深复制示例,下面是一个复杂一些的简历,里面包含了一个 WorkExperience
是另外一个类型,直接浅复制的话_workExperience 仍然指向原来的引用,来看下面的示例是怎么解决这个问题的
public class WorkExperience : ICloneable
{
public string TimePeriod { get; set; }
public string Company { get; set; }
public object Clone() => MemberwiseClone();
}
public class ComplexResume : ICloneable
{
private readonly WorkExperience _workExperience;
private string _name;
private string _email;
public ComplexResume() => _workExperience = new WorkExperience();
private ComplexResume(WorkExperience workExperience) => _workExperience = (WorkExperience)workExperience.Clone();
public void SetPersonalInfo(string name, string email)
{
_name = name;
_email = email;
}
public void SetWorkExperience(string comapny, string timePeriod)
{
_workExperience.Company = comapny;
_workExperience.TimePeriod = timePeriod;
}
public void Show()
{
Console.WriteLine($"{_name} {_email}");
Console.WriteLine($"Work Experience: {_workExperience.Company} {_workExperience.TimePeriod}");
}
public object Clone() => new ComplexResume(_workExperience)
{
_name = _name,
_email = _email
};
}
#region deep copy
var complexResume = new ComplexResume();
complexResume.SetPersonalInfo("xiaoming", "xiaoming@abc.xyz");
complexResume.SetWorkExperience("xiaomingTecch", "2001~2005");
complexResume.Show();
var complexResume1 = (ComplexResume)complexResume.Clone();
complexResume1.SetPersonalInfo("xiaohong", "xiaohong@abc.xyz");
complexResume1.Show();
#endregion deep copy
More#
有人一定会说序列化了,当然,你用序列化也是可以做到的,序列化再反序列化得到的也是一个全新的对象,但是对于简单的对象,我觉得用上面这种方式就足够了,而且这种方式是直接操作内存,把对应的数据内存复制一份更加高效
我们之前搞的推送服务里有一个推送请求的对象,会频繁的使用序列化反序列化来复制一个新的对象,这个场景就很适合使用原型模式来进行处理,高效的创建一个新的对象。
在需要频繁的复制对象的场景下,都可以考虑使用原型模式来创建新的对象
Reference#
- https://github.com/WeihanLi/DesignPatterns/tree/master/CreatePattern/PrototypePattern
- https://docs.microsoft.com/en-us/dotnet/api/system.object.memberwiseclone?view=netcore-3.1