c#中原型模式详解

基础介绍:

具体可分为2个角色:

Prototype(原型类):声明一个Clone自身的接口;

ConcretePrototype(具体原型类):,实现一个Clone自身的操作。

在原型模式中,Prototype通常提供一个包含Clone方法的接口,具体的原型ConcretePrototype使用Clone方法完成对象的创建。

**  本质:通过拷贝这些原型对象创建新的对象。**

根据其本质可以理解,原型本身就是通过一个自身的Clone方法来进行自我复制,从而产生新的对象。

比如,孙猴子吹猴毛变化多个克隆体时,就是用了原型模式,通过对自身的自我复制从而生产出N个分身。

所以从本质出发,想要实现这个功能,可以引出两个概念:其一就是浅层复制 ,再则就是深层复制

浅层复制:通过this.MemberWiseClone(),对实例的值类型进行拷贝(包含string类型),对引用类型只拷贝了引用。浅拷贝只对值类型成员进行复制,对于引用类型,只是复制了其引用,并不复制其对象。

深层复制需要通过反射和序列化来实现。

应用场景:

对象在创建(new)时,消耗资源过多繁琐耗时。

本质就是在对象的构造函数中有耗时长或者占用系统资源多的情况,

使用原型模式进行复制对象时,可以省去这些耗时耗力的操作,直接获得对象的具体实例。

最常见的使用场景之一就是对象历史节点的保存,比如在对对象进行操作一次后,进行一次复制保存当前状态(恢复到某一历史状态),可实现撤销操作。

创建方式:

  1. 原型类----用来规范具体原型

    复制代码
     1     /// <summary>
     2     /// 原型类
     3     /// </summary>
     4     public abstract class Prototype
     5     {
     6         /// <summary>
     7         /// 值类型
     8         /// </summary>
     9         public int Id { get; set; }
    10 
    11         /// <summary>
    12         /// 字符串
    13         /// </summary>
    14         public string strMessage { get; set; }
    15 
    16         /// <summary>
    17         /// 引用类型
    18         /// </summary>
    19         public Dictionary<int, string> keyValuePairs = new Dictionary<int, string>() { };
    20 
    21         /// <summary>
    22         /// 构造函数
    23         /// </summary>
    24         /// <param name="id"></param>
    25         public Prototype(int id)
    26         {
    27             this.Id = id;
    28         }
    29 
    30         /// <summary>
    31         /// 复制函数
    32         /// </summary>
    33         /// <returns></returns>
    34         public abstract Prototype Clone();
    35     }

    View Code

    通过上述代码可以看出,为了更好的展示原型类的特性,原型类中声明了值类型和引用类型来展示各自的变化。

  2. 具体原型类

    复制代码
     1     /// <summary>
     2     /// 创建具体原型
     3     /// </summary>
     4     public class ConcretePrototype : Prototype
     5     {
     6         public ConcretePrototype(int id)
     7             : base(id)
     8         { }
     9 
    10         /// <summary>
    11         /// 浅拷贝
    12         /// </summary>
    13         /// <returns></returns>
    14         public override Prototype Clone()
    15         {
    16             // 调用MemberwiseClone方法实现的是浅拷贝,另外还有深拷贝
    17             return (Prototype)this.MemberwiseClone();
    18         }
    19     }

    View Code

    通过MemberwiseClone方法实现浅拷贝,即复制值类型属性生成新的,而引用类型的属性只复制其引用,并没有生成新的。

  3. 客户端调用

    复制代码
     1     class Program
     2     {
     3         static void Main(string[] args)
     4         {
     5             ConcretePrototype concretePrototype = new ConcretePrototype(1);
     6             concretePrototype.strMessage = "AAAAAAAAA";
     7             concretePrototype.keyValuePairs.Add(1, "A");
     8             concretePrototype.keyValuePairs.Add(2, "B");
     9             Console.WriteLine("id:{0}", concretePrototype.Id);
    10             Console.WriteLine("strMessage:{0}", concretePrototype.strMessage);
    11             Console.WriteLine("keyValuePairs:");
    12             foreach (KeyValuePair<int,string> item in concretePrototype.keyValuePairs)
    13             {
    14                 Console.WriteLine("KEY:{0}  Value:{1}", item.Key, item.Value);
    15             }
    16 
    17             Console.WriteLine("\r\n");
    18 
    19             ConcretePrototype concretePrototype2 = (ConcretePrototype)concretePrototype.Clone();
    20             concretePrototype2.strMessage = "BBBBBBBBB";
    21             concretePrototype2.keyValuePairs[1] = "A1";
    22             Console.WriteLine("id:{0}", concretePrototype2.Id);
    23             Console.WriteLine("strMessage:{0}", concretePrototype2.strMessage);
    24             Console.WriteLine("keyValuePairs:");
    25             foreach (KeyValuePair<int, string> item in concretePrototype2.keyValuePairs)
    26             {
    27                 Console.WriteLine("KEY:{0}  Value:{1}", item.Key, item.Value);
    28             }
    29 
    30             Console.WriteLine("\r\n");
    31 
    32             Console.WriteLine("id:{0}", concretePrototype.Id);
    33             Console.WriteLine("strMessage:{0}", concretePrototype.strMessage);
    34             Console.WriteLine("keyValuePairs:");
    35             foreach (KeyValuePair<int, string> item in concretePrototype.keyValuePairs)
    36             {
    37                 Console.WriteLine("KEY:{0}  Value:{1}", item.Key, item.Value);
    38             }
    39             Console.ReadKey();
    40         }
    41     }

    View Code

    上述代码中,首先创建了一个concretePrototype原型对象,然后给字符串类型的strMessage赋值"AAAAAAAAA"。 然后给引用类型的keyValuePairs字典添加key=1和key=2,值分别是A和B。

    通过Clone()方法进行原型对象的复制操作,生成新对象concretePrototype2。

    修改新对象中的strMessage属性和keyValuePairs字典中key=1的值为"A1"。

    通过打印出的内容可以看出新对象中的strMessage值修改并不会影响原型对象中的内容,而引用类型keyValuePairs则发生了改变。

    通过这个实例可以看出浅复制,对值类型进行全盘拷贝,对引用类型只拷贝了引用地址。

  4. 修改上述实例,将浅复制改为深复制

    复制代码
     1     /// <summary>
     2     /// 原型类
     3     /// </summary>
     4     [Serializable]
     5     public abstract class Prototype
     6     {
     7         ......
     8     }    
     9 
    10         /// <summary>
    11     /// 创建具体原型
    12     /// 如果是要通过序列化来进行深拷贝的话,要打上Serializable标签
    13     /// </summary>
    14     [Serializable]
    15     public class ConcretePrototype : Prototype
    16     {
    17         public ConcretePrototype(int id)
    18             : base(id)
    19         { }
    20 
    21         /// <summary>
    22         /// 深拷贝
    23         /// </summary>
    24         /// <returns>返回一个全新的Person对象</returns>
    25         public override Prototype Clone()
    26         {
    27             //创建一个内存流
    28             MemoryStream ms = new MemoryStream();
    29             //创建一个二进制序列化对象
    30             BinaryFormatter bf = new BinaryFormatter();
    31             //将当前对象序列化写入ms内存流中
    32             bf.Serialize(ms, this);
    33             //设置流读取的位置
    34             ms.Position = 0;
    35             //将流反序列化为Object对象
    36             return bf.Deserialize(ms) as Prototype;
    37         }
    38     }

    View Code

    上述实例通过序列化进行深复制,当然也可以使用反射等技术进行深复制。

    运行后可以看出,深复制后引用类型也会生成一个新的地址。

总结:

原型模式就是对对象进行复制操作,而避免重复进行初始化操作,生产多个克隆对象。

相关推荐
李广坤11 小时前
状态模式(State Pattern)
设计模式
李广坤12 小时前
观察者模式(Observer Pattern)
设计模式
李广坤13 小时前
中介者模式(Mediator Pattern)
设计模式
李广坤13 小时前
迭代器模式(Iterator Pattern)
设计模式
李广坤14 小时前
解释器模式(Interpreter Pattern)
设计模式
阿无,16 小时前
java23种设计模式之前言
设计模式
Asort17 小时前
JavaScript设计模式(八):组合模式(Composite)——构建灵活可扩展的树形对象结构
前端·javascript·设计模式
数据智能老司机17 小时前
数据工程设计模式——数据基础
大数据·设计模式·架构
笨手笨脚の20 小时前
设计模式-代理模式
设计模式·代理模式·aop·动态代理·结构型设计模式
Overboom1 天前
[C++] --- 常用设计模式
开发语言·c++·设计模式