C#设计模式 之 原型模式

举报
陈言必行 发表于 2021/08/23 22:40:29 2021/08/23
【摘要】 原型模式是一种创建型设计模式, 使你能够复制已有对象, 而又无需使代码依赖它们所属的类。

别名:克隆模式、Prototype

一,意图

  用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。


二,动机

   在程序设计中,经常面临着“某些结构复杂的对象”的创建工作;由于需求的变化,这些对象经常面临着剧烈的变化,但是它们却拥有比较稳定一致的接口。

问题来了:
  如何创建易变类的实体对象?

解决方案:
  采用“原型克隆”的方法来做,它是的我们可以非常灵活地动态创建“拥有某些稳定接口”的新对象 – 所需工作仅仅是注册一个新类的对象(原型),然后在任何需要的地方不断的Clone

要点:
  原型设计模式用于隔离类对象的使用者和具体类型(易变类)之间的耦合关系,它要求这些易变类拥有稳定的接口。


三,结构

1.1

  1. 原型 (Prototype) 接口将对克隆方法进行声明。 通常情况下, 其中只会有一个名为 clone克隆的方法。
  2. 具体原型 (Concrete Prototype) 类将实现克隆方法。 除了将原始对象的数据复制到克隆体中之外, 该方法有时还需处理克隆过程中的极端情况, 例如克隆关联对象和梳理递归依赖等等。
  3. 客户端 (Client) 可以复制实现了原型接口的任何对象。

四,优缺点

优点:

  • 可以克隆对象, 而无需与它们所属的具体类相耦合。
  • 可以克隆预生成原型, 避免反复运行初始化代码。
  • 可以更方便地生成复杂对象。
  • 可以用继承以外的方式来处理复杂对象的不同配置。

缺点:

  • 克隆包含循环引用的复杂对象可能会非常麻烦。

五,应用场景

适用性:

  • 当一个系统应该独立于它的产品创建,构成和表示时,要使用原型模式;以及当要实例化的类是在运行时刻指定时,
  • 为了创建一个与产品类层次平行的工厂类层次时
  • 当一个类的实例只能有几种不同状态的组合中的一种时。建立相应数目的原型并克隆他们可能比每次用合适的状态手工实例化该类更方便。

六,代码实现

实现方式:

  1. 创建一个原型管理器:原型管理器是一个关联存储器,它返回一个与给定关键字匹配的原型。
  2. 实现克隆操作:原型设计模式最困难的部分在于正确实现Clone操作。C#语言为我们提供this.MemberwiseClone();来实现浅拷贝。而深拷贝我们可以通过序列化去实现。
  3. 初始化克隆对象:在不同的情况下,客户程序使用需要不同的初始值,所以一些原型可能要多个初始化参数。

拓展:什么是“浅拷贝和深拷贝”?

  • 浅拷贝:克隆对象和原对象共享引用类型的变量 (引用类型同指向一个内存)
  • 深拷贝:克隆一个对象时依次克隆它的实例变量 (所有数据间没有任何关系)

可以简单理解为,浅拷贝是List<int> a = new List<int>(); List<int> b = a; , 集合a,b同指向一个内存修改a中的值即修改了b中的值;而深拷贝是List<int> a = new List<int>(); List<int> b = new List<int>(a); new了一个集合b并且将a中的数据全部复制过去了,此时a,b没有任何关系。

示例代码:

class Program
{
    static void Main(string[] args)
    {
        Person p1 = new Person();
        p1.Age = 22;
        p1.Name = "Czhenya";
        p1.IdInfo = new IdInfo(111);

        // 对p1执行一个浅拷贝,并将其赋值给p2。
        Person p2 = p1.ShallowCopy();
        // 对p1做一个深度拷贝,并把它赋值给p3。
        Person p3 = p1.DeepCopy();

        Console.WriteLine("----- p1, p2, p3的原始值: -----");

        Console.WriteLine("--- 原数据 P1 :");
        DisplayValues(p1);
        Console.WriteLine("--- 浅拷贝 P2 :");
        DisplayValues(p2);
        Console.WriteLine("--- 深拷贝 P3 :");
        DisplayValues(p3);

        // 更改p1属性的值并显示p1的值
        p1.Age = 33;
        p1.Name = "Czy";
        p1.IdInfo.IdNumber = 222;

        Console.WriteLine();
        Console.WriteLine("----- 对原数据p1 进行修改后 p1、p2、p3的值: -----")

        Console.WriteLine("+++ 原数据 P1 :");
        DisplayValues(p1);
        Console.WriteLine("+++ 浅拷贝 P2 :");
        DisplayValues(p2);
        Console.WriteLine("+++ 深拷贝 P3 :");
        DisplayValues(p3);

        Console.ReadKey();
    }

    static void DisplayValues(Person p)
    {
        Console.WriteLine("姓名: {0:s}, 年龄: {1:d}, ID: {2:d}", p.Name, p.Age
    }

}

/// <summary>
/// 原类型 -- 需要被拷贝的类
/// </summary>
public class Person
{
    // 多种类型数据,查看拷贝后的数据
    public int Age;
    public string Name;
    public IdInfo IdInfo;

    /// <summary>
    /// 浅拷贝
    /// MemberwiseClone -- 按成员拷贝(复制引用类型的地址,而不是new)
    /// </summary>
    /// <returns></returns>
    public Person ShallowCopy()
    {
        return (Person)this.MemberwiseClone();
    }

    /// <summary>
    /// 深拷贝
    /// </summary>
    /// <returns></returns>
    public Person DeepCopy()
    {
        Person clone = (Person)this.MemberwiseClone();
        // int Age 这种简单类型不需要管,在MemberwiseClone处理了
        // 引用类型
        clone.IdInfo = new IdInfo(IdInfo.IdNumber);
        return clone;
    }
}

// 作为引用类型的数据
public class IdInfo
{
    public int IdNumber;

    public IdInfo(int idNumber)
    {
        this.IdNumber = idNumber;
    }
}

测试结果:
1.2


设计模式系列博文示例代码工程:链接



【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请自行联系原作者进行授权。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。