原型模式(浅克隆和深克隆)

原型模式是一种创建型设计模式,它通过复制一个已经存在的实例来返回新的实例,而不是新建实例.被复制的实例就是我们所称的原型,这个原型是可定制的.
原型模式多用于创建复杂的或者耗时的实例, 因为这种情况下,复制一个已经存在的实例可以使程序运行更高效,或者创建值相等,只是命名不一样的同类数据.

原型模式中的拷贝分为"浅拷贝"和"深拷贝":
浅克隆: 对值类型的成员变量进行值的复制,对引用类型的成员变量只复制引用,不复制引用的对象.
深克隆: 对值类型的成员变量进行值的复制,对引用类型的成员变量也进行引用对象的复制.

首先来看一些原型模式的实现代码,这里先运用浅克隆来实现 ,首先建立一个类来表示班级

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Threading;
 4 using System.Threading.Tasks;
 5 
 6 namespace 设计模式之原型模式
 7 {
 8     /// <summary>
 9     /// 班级类
10     /// </summary>
11     public class Class
12     {
13         public int Num { get; set; }
14         public string Remark { get; set; }
15     }
16 
17     /// <summary>
18     /// 学生类
19     /// </summary>
20     class StudentPrototype
21     {
22         //模拟复杂的构造函数
23         private StudentPrototype()
24         {
25             Thread.Sleep(2000);
26             long lResult = 0;
27             for (int i = 0; i < 1000000000; i++)
28             {
29                 lResult += i;
30             }
31             Console.WriteLine("{0}被构造..", this.GetType().Name);
32         }
33 
34         private static StudentPrototype _StudentPrototype = null;
35 
36         static StudentPrototype()
37         {
38             _StudentPrototype = new StudentPrototype()
39             {
40                 Id = 337,
41                 Name = "学友",
42                 Class=new Class()
43                 {
44                     Num=1,
45                     Remark="BIGDong"
46                 }
47             };
48         }
49         /// <summary>
50         /// 克隆出实体类
51         /// </summary>
52         /// <returns></returns>
53         public static StudentPrototype CreatInstance()
54         {
55             //克隆一个对象(浅克隆)
56             StudentPrototype studentPrototype = (StudentPrototype)_StudentPrototype.MemberwiseClone();
57           
58             return studentPrototype;
59         }
60 
61         public int Id { get; set; }
62         public string Name { get; set; }
63         public Class Class{ get; set; }
64 
65         public void Study()
66         {
67             Console.WriteLine("{0}在学习设计模式",this.Name);
68         }
69     }
70 }

实例类可以调用MemberwiseClone函数来为该实体类克隆出一个副本,那么我们来做下测试

 1 namespace 设计模式之原型模式
 2 {
 3     class Program
 4     {
 5         static void Main(string[] args)
 6         {
 7             StudentPrototype stundet1 = StudentPrototype.CreatInstance();
 8             StudentPrototype stundet2 = StudentPrototype.CreatInstance();
 9             stundet1.Name = "华仔";
10             stundet1.Id = 2;
11             stundet1.Class.Num = 3;
12             stundet1.Class.Remark = "郭富城";
13 
14             Console.WriteLine(string.Format("stundet1.Name是{0},stundet1.Id是{1},stundet1.Class.Num是{2},stundet1.Class.Remark是{3},"
15             , stundet1.Name, stundet1.Id, stundet1.Class.Num, stundet1.Class.Remark));
16 
17             Console.WriteLine(string.Format("stundet2.Name是{0},stundet2.Id是{1},stundet2.Class.Num是{2},stundet2.Class.Remark是{3},"
18         , stundet2.Name, stundet2.Id, stundet2.Class.Num, stundet2.Class.Remark));
19 
20             Console.ReadKey();
21         }
22     }
23 }

 

从调试可以看出这两个被使用浅克隆生成的对象,当第一个被对象修改值的时候,第二个对象里面的值对象并没有被覆盖掉,而第二个对象里的class引用类全被改变了

说到这里就要解释下,引用类型指向都是一个内存地址,而这个内存地址指向的是推里的一个值,上面的例子创建了两个对象都是指向同一块内存地址,所以只要改变一个的值

另外一个也会跟着改变(这和单例模式很像),然后这里还有一点说明,在StudentPrototype类里的string字段也是引用类型,为什么它没被覆盖掉呢?

在解释这个问题之前,我们上一段代码来解决上面的class不会被覆盖的,然后再解释string为什么不会被覆盖的问题

把上面赋值的地方之间生成一个新的对象,这样就将内存地址指向一个新的值

我们可以看到 现在class不会被覆盖掉了,其实string每次在赋值的时候也是这样子,重新生成一个内存地址,指向一个新的值,所以也不会被覆盖

而这种方式也正是我们讲的深克隆

我们也可以在类的构造函数里直接添加深克隆的代码,但是这样每一个新的类就得创建在构造函数写一大堆,是麻烦的,其实我们可以使用序列化的方式

来实习这一繁琐的步骤,首先要在类前加上序列化的特性

然后写一个创建序列化和反序列化类,通过反序列出来的对象都是重新生成的

 1 namespace 设计模式之原型模式
 2 {
 3     class SerializeHelper
 4     {
 5         /// <summary>
 6         /// 将一个对象转成字符串(序列化)
 7         /// </summary>
 8         /// <param name="targat">对象</param>
 9         /// <returns></returns>
10         public static string Serializble(object targat)
11         {
12             using (MemoryStream stream = new MemoryStream())
13             {
14                 new BinaryFormatter().Serialize(stream, targat);
15                 return Convert.ToBase64String(stream.ToArray());
16             }
17         }
18 
19         /// <summary>
20         /// 将字符串转成对象(反序列化)
21         /// </summary>
22         /// <typeparam name="T"></typeparam>
23         /// <param name="target">字符串</param>
24         /// <returns></returns>
25         public static T Derializable<T>(string target)
26         {
27             byte[] targetArray = Convert.FromBase64String(target);
28             using (MemoryStream stream = new MemoryStream(targetArray))
29             {
30                 return (T)(new BinaryFormatter().Deserialize(stream));
31             }
32         }
33 
34         /// <summary>
35         /// 外界调用接口
36         /// </summary>
37         /// <typeparam name="T">泛型</typeparam>
38         /// <param name="t"></param>
39         /// <returns></returns>
40         public static T DeepClone<T>(T t)
41         {
42             return Derializable<T>(Serializble(t));
43         }
44 
45     }
46 }

然后获得实例类的GetInstance函数里调用

这样输出的结果也就是深克隆要的效果了

原文地址:https://www.cnblogs.com/BigDong/p/8045104.html