6.原型模式

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

×

喜欢就点赞,疼爱就打赏