c#中原型模式详解
基础介绍:
具体可分为2个角色:
Prototype(原型类):声明一个Clone自身的接口;
ConcretePrototype(具体原型类):,实现一个Clone自身的操作。
在原型模式中,Prototype通常提供一个包含Clone方法的接口,具体的原型ConcretePrototype使用Clone方法完成对象的创建。
本质:通过拷贝这些原型对象创建新的对象。
根据其本质可以理解,原型本身就是通过一个自身的Clone方法来进行自我复制,从而产生新的对象。
比如,孙猴子吹猴毛变化多个克隆体时,就是用了原型模式,通过对自身的自我复制从而生产出N个分身。
所以从本质出发,想要实现这个功能,可以引出两个概念:其一就是浅层复制,再则就是深层复制。
浅层复制:通过this.MemberWiseClone(),对实例的值类型进行拷贝(包含string类型),对引用类型只拷贝了引用。浅拷贝只对值类型成员进行复制,对于引用类型,只是复制了其引用,并不复制其对象。
深层复制:需要通过反射和序列化来实现。
应用场景:
对象在创建(new)时,消耗资源过多或繁琐耗时。
本质就是在对象的构造函数中有耗时长或者占用系统资源多的情况,
使用原型模式进行复制对象时,可以省去这些耗时耗力的操作,直接获得对象的具体实例。
最常见的使用场景之一就是对象历史节点的保存,比如在对对象进行操作一次后,进行一次复制保存当前状态(恢复到某一历史状态),可实现撤销操作。
创建方式:
原型类----用来规范具体原型
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 }通过上述代码可以看出,为了更好的展示原型类的特性,原型类中声明了值类型和引用类型来展示各自的变化。
具体原型类
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 }通过MemberwiseClone方法实现浅拷贝,即复制值类型属性生成新的,而引用类型的属性只复制其引用,并没有生成新的。
客户端调用
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 }上述代码中,首先创建了一个concretePrototype原型对象,然后给字符串类型的strMessage赋值“AAAAAAAAA”。 然后给引用类型的keyValuePairs字典添加key=1和key=2,值分别是A和B。
通过Clone()方法进行原型对象的复制操作,生成新对象concretePrototype2。
修改新对象中的strMessage属性和keyValuePairs字典中key=1的值为“A1”。
通过打印出的内容可以看出新对象中的strMessage值修改并不会影响原型对象中的内容,而引用类型keyValuePairs则发生了改变。
通过这个实例可以看出浅复制,对值类型进行全盘拷贝,对引用类型只拷贝了引用地址。
修改上述实例,将浅复制改为深复制
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 }上述实例通过序列化进行深复制,当然也可以使用反射等技术进行深复制。
运行后可以看出,深复制后引用类型也会生成一个新的地址。
总结:
原型模式就是对对象进行复制操作,而避免重复进行初始化操作,生产多个克隆对象。
c#中原型模式详解的更多相关文章
- (二十三)原型模式详解(clone方法源码的简单剖析)
作者:zuoxiaolong8810(左潇龙),转载请注明出处,特别说明:本博文来自博主原博客,为保证新博客中博文的完整性,特复制到此留存,如需转载请注明新博客地址即可. 原型模式算是JAVA中最简单 ...
- 设计模式之 原型模式详解(clone方法源码的简单剖析)
作者:zuoxiaolong8810(左潇龙),转载请注明出处,特别说明:本博文来自博主原博客,为保证新博客中博文的完整性,特复制到此留存,如需转载请注明新博客地址即可. 原型模式算是JAVA中最简单 ...
- 二十三:原型模式详解(clone复制方法源码)
定义:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象. 定义比较简单,总结一下是通过实例指定种类,通过拷贝创建对象. 在JAVA语言中使用原型模式是非常 ...
- JAVA 设计模式之 原型模式详解
原型模式(Prototype Pattern)是指原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象. 原型模式利用的是克隆的原理,创建新的对象,JDK提供的Cloneable 和JSON. ...
- 深入浅出的webpack构建工具---devTool中SourceMap模式详解(四)
阅读目录 一:什么是SourceMap? 二:理解webpack中的SourceMap的eval,inline,sourceMap,cheap,module 三:开发环境和线上环境如何选择source ...
- Javascript设计模式之装饰者模式详解篇
一.前言: 装饰者模式(Decorator Pattern):在不改变原类和继承的情况下动态扩展对象功能,通过包装一个对象来实现一个新的具有原对象相同接口的新的对象. 装饰者模式的特点: 1. 在不改 ...
- 你不知道的JavaScript--Item15 prototype原型和原型链详解
用过JavaScript的同学们肯定都对prototype如雷贯耳,但是这究竟是个什么东西却让初学者莫衷一是,只知道函数都会有一个prototype属性,可以为其添加函数供实例访问,其它的就不清楚了, ...
- Javascript中prototype属性详解 (存)
Javascript中prototype属性详解 在典型的面向对象的语言中,如java,都存在类(class)的概念,类就是对象的模板,对象就是类的实例.但是在Javascript语言体系中,是不 ...
- 从mixin到new和prototype:Javascript原型机制详解
从mixin到new和prototype:Javascript原型机制详解 这是一篇markdown格式的文章,更好的阅读体验请访问我的github,移动端请访问我的博客 继承是为了实现方法的复用 ...
- 【转载】C/C++中extern关键字详解
1 基本解释:extern可以置于变量或者函数前,以标示变量或者函数的定义在别的文件中,提示编译器遇到此变量和函数时在其他模块中寻找其定义.此外extern也可用来进行链接指定. 也就是说extern ...
随机推荐
- 【Git】常用命令汇总
一.仓库管理 git init:本地初始化 git clone:克隆远程仓库 git remote:远程仓库管理 git remote:查看远程仓库的信息 git remote -v:显示更详细的信息 ...
- 图像处理评价指标_划分系数Vpc划分熵Vpe
划分系数划分熵 评价指标划分系数Vpc和划分熵Vpe能够反映分割矩阵的模糊程度,Vpc数值越大,分割矩阵的模糊性越小,分割效果越好:Vpe数值越小,像素分类越准确,分割效果越好. (1)划分系数Vpc ...
- 《逆向工程核心原理》之DLL注入
DLL注入 DLL注入指的是向运行中的其他进程强制插入特定的DLL文件.从技术细节来说,DLL注入命令其他进程自行调用LoadLibrary() API,加载(Loading)用户指定的DLL文件.D ...
- keycloak~AbstractJsonUserAttributeMapper的作用
AbstractJsonUserAttributeMapper 它是一个抽象类,用来更新条件更新用户属性(user_attribute)的信息,我们在实现自己的mapper时,需要关注3个方法,下面分 ...
- Redis的设计与实现(4)-跳跃表
跳跃表 (skiplist) 是一种有序数据结构, 它通过在每个节点中维持多个指向其他节点的指针, 从而达到快速访问节点的目的. 跳跃表支持平均 O(log N) 最坏 O(N) 复杂度的节点查找, ...
- win10安装mysql时提示错误:mysqld: Can't change dir to 'C: oftware\mysql\data\' (Errcode: 2 - No such file or directory)
win10安装解压版mysql时,提示错误: 2019-10-22 09:02:00 2004 [ERROR] Can't find messagefile 'C:\WINDOWS\system32\ ...
- kafka分区分配策略
前言 现有主流消息中间件都是生产者-消费者模型,主要角色都是:Producer -> Broker -> Consumer,上手起来非常简单,但仍有需要知识点需要我们关注,才能避免一些错误 ...
- 为什么Python是数据科学家的首选语言
这篇文章全面探讨了Python作为数据科学领域首选语言的原因.从Python的历史.特性,到在数据科学中的应用实例,再到与其他数据科学语言的比较,以及在实际企业中的应用,我们深入剖析了Python的优 ...
- 从序号和确认号理解TCP三次握手
头部信息 TCP首部存储的数据和建立连接有关,具体每个字段的用途可以参考这一篇文章,其中序号和确认号决定了发送数据的内容. 头部中间部分"保留"和"窗口"中间是 ...
- Go 如何正确关闭通道
序言 Go 在通道这一块,没有内置函数判断通道是否已经关闭,也没有可以直接获取当前通道数量的方法.所以对于通道,Go 显示的不是那么优雅.另外,如果对通道进行了错误的使用,将会直接引发系统 panic ...