6.创建型模式-原型模式
6.1 基础知识
学习难度:3
使用频率:3
总分:6
定义
原型模式(Prototype Pattern)用原型实例指定创建对象的种类,并且通过复制这些原型创建新的对象。
说人话
复制传入的对象
结构图
实现步骤
- 原型接口:定义克隆方法返回对象
- 具体原型类:实现原型接口,实例化新的具体原型类,赋值让成员的值相同并返回
- 浅拷贝:克隆方法中返回新实例化的当前类对象
- 深拷贝:先新实例化的当前类对象,让对象的引用类型是重新实例化的,防止指向的堆内存相同,再返回
- 客户端:用具体原型的克隆方法得到新的具体原型对象
说明
C#已经帮我们定义好了原型接口 ICloneable,接口中定义了克隆方法,可以不需要我们自己定义。
this.MemberwiseClone() 是C#定义好的克隆对象的方法,可以直接返回当前对象的克隆对象,就不用自己手动 new 了。
6.2 模版代码
原型接口、具体原型类和深拷贝原型类
public interface IPrototype
{
object Clone();
}
public class DeepCopyObjectPrototype : IPrototype
{
public int SomeProperty { get; set; }
public object Clone()
{
return new DeepCopyObjectPrototype
{
SomeProperty = this.SomeProperty
};
}
}
public class ConcretePrototype : IPrototype
{
public int Property1 { get; set; }
public string Property2 { get; set; }
//有引用类型对象 克隆时需要深度拷贝
public DeepCopyObjectPrototype Property3 { get; set; }
public ConcretePrototype(int property1, string property2, DeepCopyObjectPrototype property3)
{
this.Property1 = property1;
this.Property2 = property2;
this.Property3 = property3;
}
public object Clone()
{
// 在这里执行深拷贝 拷贝引用类型对象
DeepCopyObjectPrototype deepCopyObjectPrototype = (DeepCopyObjectPrototype)this.Property3.Clone();
return new ConcretePrototype(this.Property1, this.Property2, deepCopyObjectPrototype);
}
}
客户端
class Program
{
static void Main(string[] args)
{
// 创建原型对象
var original = new ConcretePrototype(42, "原始对象", new DeepCopyObjectPrototype { SomeProperty = 100 });
// 克隆原型对象
var clone = (ConcretePrototype)original.Clone();
// 修改克隆对象的属性,不影响原型对象
clone.Property1 = 24;
clone.Property2 = "克隆对象";
clone.Property3.SomeProperty = 200;
Console.WriteLine("原始对象: Property1={0}, Property2={1}, Property3.SomeProperty={2}",
original.Property1, original.Property2, original.Property3.SomeProperty);
// 原始对象: Property1=42, Property2=原始对象, Property3.SomeProperty=100
Console.WriteLine("克隆对象: Property1={0}, Property2={1}, Property3.SomeProperty={2}",
clone.Property1, clone.Property2, clone.Property3.SomeProperty);
// 克隆对象: Property1=24, Property2=克隆对象, Property3.SomeProperty=200
}
}
6.3 CSharp实践
实践需求
使用原型模式。
写一个简历类。
简历类中必须要有姓名,可以设置性别和年龄,可以设置工作经历,并且可展示。
客户端需要写三份简历。后两份简历是复制第一份简历的基础上再修改。
工作经历类
// 工作经历类
class WorkExperience : ICloneable
{
// 工作日期
private string workDate;
public string WorkDate
{
get { return workDate; }
set { workDate = value; }
}
// 公司名称
private string company;
public string Company
{
get { return company; }
set { company = value; }
}
// 实现ICloneable接口,返回对象的浅表副本
public Object Clone()
{
return (Object)this.MemberwiseClone();
}
}
简历类
//简历类
class Resume : ICloneable
{
private string name; // 姓名
private string sex; // 性别
private string age; // 年龄
private WorkExperience workExperience; // 工作经历
//公共构造函数 传入名字 new出工作经历对象
public Resume(string name)
{
this.name = name;
workExperience = new WorkExperience();
}
//私有构造函数 在简历类的克隆方法中会被调用 返回新的简历对象 调用工作经历对象的克隆方法实现深拷贝
private Resume(WorkExperience work)
{
this.workExperience = (WorkExperience)work.Clone();
}
// 设置个人信息
public void SetPersonalInfo(string sex, string age)
{
this.sex = sex;
this.age = age;
}
// 设置工作经历
public void SetWorkExperience(string workDate, string company)
{
workExperience.WorkDate = workDate;
workExperience.Company = company;
}
// 显示个人信息和工作经历
public void Display()
{
Console.WriteLine("{0} {1} {2}", name, sex, age);
Console.WriteLine("工作经历:{0} {1}", workExperience.WorkDate, workExperience.Company);
}
// 实现ICloneable接口,返回对象的浅表副本
public Object Clone()
{
Resume obj = new Resume(this.workExperience); // 先复制工作经历
obj.name = this.name; // 复制姓名、性别和年龄
obj.sex = this.sex;
obj.age = this.age;
return obj;
}
}
客户端
class Program
{
static void Main(string[] args)
{
// 创建一个名为"大鸟"的简历对象a,并设置其个人信息和工作经历
Resume bigBird1 = new Resume("大鸟");
bigBird1.SetPersonalInfo("男", "29");
bigBird1.SetWorkExperience("1998-2000", "XX公司");
// 将a复制给b,然后修改b的工作经历
Resume bigBird2 = (Resume)bigBird1.Clone();
bigBird2.SetWorkExperience("1998-2006", "YY企业");
// 将a复制给c,然后修改c的工作经历
Resume bigBird3 = (Resume)bigBird1.Clone();
bigBird3.SetWorkExperience("1998-2003", "ZZ企业");
// 显示三个简历对象的信息
bigBird1.Display();
// 大鸟 男 29
// 工作经历:1998-2000 XX公司
bigBird2.Display();
// 大鸟 男 29
// 工作经历:1998-2006 YY企业
bigBird3.Display();
// 大鸟 男 29
// 工作经历:1998-2003 ZZ企业
}
}
6.4 Unity实践
实践需求
使用原型模式,克隆暗夜猎手薇恩和无极剑圣易
抽象英雄原型类和具体英雄原型类
public abstract class HeroPrototype : ICloneable
{
public const string heroPrefabConstPath = "Lesson07_创建型模式_原型模式";
public string heroName;
public GameObject heroPrefab;
// 修改构造函数,使用一个参数以设置英雄名字
protected HeroPrototype(string name)
{
heroName = name;
Debug.Log($"创建英雄,英雄名字是 {heroName}");
heroPrefab = Resources.Load<GameObject>(heroPrefabConstPath + "/" + heroName);
GameObject.Instantiate(heroPrefab,
new Vector3(Random.Range(-3, 3), Random.Range(-3, 3), Random.Range(-3, 3)), Quaternion.identity);
}
public virtual object Clone()
{
Debug.Log($"克隆英雄,英雄名字是 {heroName}");
return GameObject.Instantiate(heroPrefab,
new Vector3(Random.Range(-3, 3), Random.Range(-3, 3), Random.Range(-3, 3)), Quaternion.identity);
}
}
public class YiPrototype : HeroPrototype
{
public YiPrototype() : base("Yi")
{
}
}
public class VaynePrototype : HeroPrototype
{
public VaynePrototype() : base("Vayne")
{
}
}
客户端
public class TestPrototypePattern : MonoBehaviour
{
void Start()
{
HeroPrototype heroPrototype1 = new VaynePrototype();
heroPrototype1.Clone();
HeroPrototype heroPrototype2 = new YiPrototype();
heroPrototype2.Clone();
}
}
运行结果
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com