小看--原型模式
前面我们说过了单例模式,是用来强制保证同一个进程内只有一个对象;享元模式:利用第三方工厂来创建对象,也可以保证一个进程内只有一个对象(非强制保证);那么今天我们来讲讲原型模式;先不说概念了,直接看下面一个例子;
(一) 原型模式
下面是一个StudentSingleton,里面是可以保证一个进程内只有一个对象了的(简单的保证)
public class StudentSingleton { private StudentSingleton() { Thread.Sleep(2000); long lResult = 0; for (var i = 0; i <= 100000; i++) { lResult += i; } Console.WriteLine($"{this.GetType().Name}被构造"); } /// <summary> /// 单例 /// </summary> private static StudentSingleton _studentSingleton=new StudentSingleton(); public static StudentSingleton CreateInstance() { return _studentSingleton; } }
下面我们在两个地方要用到这个StudentSingleton的对象;
var studentSingleTone1 = StudentSingleton.CreateInstance(); studentSingleTone1.Name = "zhangzhen"; studentSingleTone1.Id = "111"; Console.WriteLine("第一个对象的Name"+studentSingleTone1.Name); Console.WriteLine("第一个对象的Id" + studentSingleTone1.Id); var studentSingleTone2 = StudentSingleton.CreateInstance(); studentSingleTone2.Name = "1zhangzhen1"; studentSingleTone2.Id = "222"; Console.WriteLine("第二个对象的Name" + studentSingleTone2.Name); Console.WriteLine("第二个对象的Id" + studentSingleTone2.Id); Console.WriteLine("----------------"); Console.WriteLine("第一个对象的Name" + studentSingleTone1.Name); Console.WriteLine("第一个对象的Id" + studentSingleTone1.Id);
第二个对象的值改变和会影响第一个(这个也是单例模式的缺点),于是我们就提出,有没有这样一种方法,能够保证大家一起修改,不会互相影响呢?----------接下来解决这个问题就是原型模式,原型模式是为了解决同一个进程如何保证只被构造一次,然后还可以做到互不影响(对象重用);
(二)浅拷贝
那接下来就来解决我们刚刚说的那个问题,上面的单例模式,虽然做到了同一个进程只有一个对象,但是对象操作相互影响。
原型模式:1、保证对象同一个进程内,只被构造一次。2、还要做到对象操作相互不影响。
解决思路就是在单例模式的基础,既然要做到对象操作相互不影响,那么就每次创建对象返回的时候,返回一份拷贝。
public class StudentPrototype { public string Id { get; set; } public string Name { get; set; } private StudentPrototype() { Thread.Sleep(2000); long lResult = 0; for (var i = 0; i <= 100000; i++) { lResult += i; } Console.WriteLine($"{this.GetType().Name}被构造"); } /// <summary> /// 单例 /// </summary> private static StudentPrototype _studentPrototype =new StudentPrototype(); public static StudentPrototype CreateInstance() { StudentPrototype sutStudentPrototype =(StudentPrototype) _studentPrototype.MemberwiseClone(); return sutStudentPrototype; } }
上面这个例子,我们做了一份内存拷贝之后,就成功的实现了对象只被构造一次,操作之间互不影响。
(三) 深拷贝
继续刚刚的例子,我们给StudentPrototype加上一属性叫做班级(Class);
public class StudentPrototype { public string Id { get; set; } public string Name { get; set; } public Class Class { get; set; } private StudentPrototype() { Thread.Sleep(2000); long lResult = 0; for (var i = 0; i <= 100000; i++) { lResult += i; } Console.WriteLine($"{this.GetType().Name}被构造"); } /// <summary> /// 单例 /// </summary> private static StudentPrototype _studentPrototype =new StudentPrototype() { Id = "111", Name = "Fool", Class = new Class() { ClassId = 3, ClassName = "测试" } }; public static StudentPrototype CreateInstance() { StudentPrototype sutStudentPrototype =(StudentPrototype) _studentPrototype.MemberwiseClone(); return sutStudentPrototype; } } public class Class { public int ClassId { get; set; } public string ClassName { get; set; } }
又发现对于引用类型呢,我们之前做法是失效的,这是为啥呢,因为我们上面的MemberwiseClone()方法是可以实现浅拷贝,就是只是复制了对象的引用(没有复制到具体的值)。大概过程如下
这个就是浅拷贝,浅拷贝拷贝的是对象的引用(没有把整个对象一起拷贝过来);浅拷贝只是复制对象本身(就是该对象所在堆中的一块连续地址内容)
深拷贝就是把整个对象都一起拷贝过来。
对于C#来说,值类型的复制就是全盘复制(string),引用类型的复制,浅拷贝是只复制引用。
对我们来说,如何实现对引用类型的全盘复制才是最主要的。如何实现深拷贝。
---接着我们的案例,我们暂时不说如何深拷贝先,我们先讲一些其他做法先。
竟然是因为引用类型只是复制引用,那我们每次拷贝的是去改变它的引用不就可以做到了互不影响吗,基于这个思路,我们代码做如下改变;
这样子也能够实现了我们的对引用类型的拷贝。
但是就像很多人说的那样,这种做法不太好,不太合理。因为你类里面嵌套多个类,多个类里面又嵌套很多的时候,这样做就会非常尴尬。所以下面来介绍深拷贝;
就是利用序列化和反序列化来实现---注意:因为我们要进行序列化,需要给对应的类加上 [Serializable];
/// <summary> /// 序列化和反序列化实现 /// </summary> public class SerializeHelper { public static T DeepClone<T>(T t) { T tObj; //把对象序列化到内存。 using (MemoryStream memoryStream = new MemoryStream()){ BinaryFormatter bf=new BinaryFormatter(); bf.Serialize(memoryStream,t); memoryStream.Seek(0, SeekOrigin.Begin); tObj=(T)bf.Deserialize(memoryStream); memoryStream.Close(); } return tObj; } }
(四)为啥string是引用类型,浅拷贝的时候,会拷贝到值;
简单的讲就是,字符串的不可变性,每次给字符串进行赋值是,其实是发生了这些操作,先看看内存里面是否存在相同的字符串,如果不存在就去开辟一个新的地址,那所以说,这就是为毛字符串复制的时候可以复制到值,因为字符串的值如果不同的话,就会去新开辟一个内存。
总结:原型模式也是一种对象复用技术,希望通过上面能够对加深大家对原型模式的理解。