原型模式是一种创建型设计模式,它通过复制一个已经存在的实例来返回新的实例,而不是新建实例.被复制的实例就是我们所称的原型,这个原型是可定制的.
原型模式多用于创建复杂的或者耗时的实例, 因为这种情况下,复制一个已经存在的实例可以使程序运行更高效,或者创建值相等,只是命名不一样的同类数据.
原型模式中的拷贝分为"浅拷贝"和"深拷贝":
浅克隆: 对值类型的成员变量进行值的复制,对引用类型的成员变量只复制引用,不复制引用的对象.
深克隆: 对值类型的成员变量进行值的复制,对引用类型的成员变量也进行引用对象的复制.
首先来看一些原型模式的实现代码,这里先运用浅克隆来实现 ,首先建立一个类来表示班级
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 }