C#设计模式学习笔记:(5)原型模式
本笔记摘抄自:https://www.cnblogs.com/PatrickLiu/p/7640873.html,记录一下学习过程以备后续查用。
一、引言
很多人说原型设计模式会节省机器内存,他们说是拷贝出来的对象是原型的复制,不会使用内存。我认为这是不对的,因为拷贝出来的每一个对象都是实际
存在的,每个对象都有自己独立的内存地址且会被GC回收。如果就浅拷贝来说,可能会公用一些字段(引用类型),但深拷贝是不会的。所以说原型设计模式会
提高内存使用率是不一定的,具体还要看当时的设计,如果拷贝出来的对象缓存了,每次使用的是缓存的拷贝对象,那就另当别论,再说该模式本身解决的不
是内存使用率的问题。
附:浅复制与深复制的区别 浅复制一个对象: 1)如果这个对象(如int age=18 )是值类型,则得到的对象是一个全新的值类型对象(新的内存地址); 2)如果这个对象是引用类型(如class Person): I、这个对象中的值类型(如person.Age=18)是一个全新的值类型对象(新的内存地址); II、这个对象中的引用类型是公用的(同一内存地址),当原始引用类型的值变化时,新生成对象的引用类型的值也会跟着变化。 深复制一个对象: 无论之前这个对象是值类型还是引用类型,得到的新对象都是一个全新的对象(新的内存地址)。
在软件系统中,当创建一个类的实例的过程很昂贵或很复杂,并且我们需要创建多个这样的类的实例时,如果用new操作符去创建时,会增加创建的复杂度
与客户代码的耦合度。如果采用工厂方法模式来创建这样的实例对象的话,随着产品类的不断增加,导致子类的数量不断增多,也导致了相应工厂类的增加,
系统复杂程度随之增加,所以此时使用工厂方法模式来封装类的创建过程并不合适。
由于每个类的实例都是相同的(这个相同指的是类型相同,但是每个实例的状态参数会有不同,如果状态数值也相同就没意义了),有一个这样的对象就可
以了。当我们需要多个相同的类实例时,可以通过对原来对象拷贝一份来完成创建,这个思路正是原型模式的实现方式。
二、原型模式介绍
原型模式:英文名称--Prototype Pattern;分类--创建型。
2.1、动机(Motivate)
在软件系统中,经常面临着“某些结构复杂的对象”的创建工作,由于需求的变化,这些对象经常面临着剧烈的变化,但是它们却拥有比较稳定一致的接口。
如何应对这种变化?如何向“客户程序(使用这些对象的程序)”隔离出“这些易变对象”,从而使得“依赖这些易变对象的客户程序”不随着需求改变而改变?
2.2、意图(Intent)
使用原型实例指定创建对象的种类,然后通过拷贝这些原型来创建新的对象。--《设计模式》Gof
2.3、结构图(Structure)
2.4、模式的组成
从上图可以看出,在原型模式的结构图有以下角色:
1)原型类(Prototype):原型类,声明一个Clone自身的接口。
2)具体原型类(ConcretePrototype):实现一个Clone自身的操作。
在原型模式中,Prototype通常提供一个包含Clone方法的接口,具体的原型ConcretePrototype使用Clone方法完成对象的创建。
2.5、原型模式的具体实现
《大话西游之大圣娶亲》这部电影,里面有这样一个场景:牛魔王使用无敌牛虱大战至尊宝,至尊宝的应对之策就是--从脑后拔下一撮猴毛,吹了口仙气,
无数猴子猴孙现身来大战牛魔王的无敌牛虱。至尊宝的猴子猴孙就是该原型模式的最好体现,至尊宝创建自己的一个副本,不用还要重新孕育五百年,然后出
世、再学艺,最后再来和老牛大战,假如这样的话,估计黄花菜都凉了。至尊宝有3根救命猴毛,轻轻一吹,想要多少个自己就有多少个,方便、快捷。
class Program
{
/// <summary>
/// 抽象原型,定义了原型本身所具有特征和动作,该类型就是至尊宝。
/// </summary>
public abstract class Prototype
{
//战斗--保护师傅
public abstract void Fight();
//化缘--不要饿着师傅
public abstract void BegAlms(); //吹口仙气--变一个自己出来
public abstract Prototype Clone();
} /// <summary>
/// 具体原型,例如:行者孙A,他只负责与从天界宠物下界的妖怪战斗和化缘斋饭食。
/// </summary>
public sealed class MonkeyKingPrototype : Prototype
{
//战斗--保护师傅
public override void Fight()
{
Console.WriteLine("七十二变,集万千武艺于一身。");
}
//化缘--不要饿着师傅
public override void BegAlms()
{
Console.WriteLine("阿弥陀佛!施主,请施舍点饭食。");
} //吹口仙气--变一个自己出来
public override Prototype Clone()
{
return (MonkeyKingPrototype)MemberwiseClone();
}
} /// <summary>
/// 具体原型,例如:孙行者B,他只负责与自然界修炼成妖的妖怪战斗和化缘水果。
/// </summary>
public sealed class NewskyPrototype : Prototype
{
//战斗--保护师傅
public override void Fight()
{
Console.WriteLine("七十二变,集万千武艺于一身。");
}
//化缘--不要饿着师傅
public override void BegAlms()
{
Console.WriteLine("阿弥陀佛!施主,请施舍点水果。");
} //吹口仙气--变一个自己出来
public override Prototype Clone()
{
return (NewskyPrototype)MemberwiseClone();
}
} static void Main(string[] args)
{
#region 原型模式
Prototype monkeyKing = new MonkeyKingPrototype();
Prototype monkeyKing1 = monkeyKing.Clone();
Prototype monkeyKing2 = monkeyKing.Clone(); Prototype newsky = new NewskyPrototype();
Prototype newsky1 = newsky.Clone();
Prototype newsky2 = newsky.Clone(); //孙行者A打妖怪
monkeyKing1.Fight();
//孙行者B去化缘
newsky2.BegAlms(); Console.Read();
#endregion
}
}
运行结果如下:
三、原型模式的实现要点
Prototype模式同样用于隔离类对象的使用者和具体类型(易变类)之间的耦合关系,它同样要求这些“易变类”拥有“稳定的接口”。
Prototype模式对于“如何创建易变类的实体对象”(创建型模式除了Singleton模式以外,都是用于解决创建易变类的实体对象的问题的)采用“原型克隆”的方
法来做,它使得我们可以非常灵活地动态创建“拥有某些稳定接口”的新对象——所需工作仅仅是注册一个新类的对象(即原型),然后在任何需要的地方不断
地Clone。
Prototype模式中的Clone方法可以利用.NET中的Object类的MemberwiseClone()方法或者序列化来实现深拷贝。
3.1、原型模式的优点
1)原型模式向客户隐藏了创建新实例的复杂性。
2)原型模式允许动态增加或较少产品类。
3)原型模式简化了实例的创建结构,工厂方法模式需要有一个与产品类等级结构相同的等级结构,而原型模式不需要这样。
4)产品类不需要事先确定产品的等级结构,因为原型模式适用于任何的等级结构。
3.2、原型模式的缺点
1)每个类必须配备一个克隆方法。
2)配备克隆方法需要对类的功能进行通盘考虑,这对于全新的类不是很难,但对于已有的类不一定很容易,特别当一个类引用不支持串行化的间接对象,或
者引用含有循环结构的时候。
3.3、原型模式的使用场景
1)资源优化场景
类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等。
2)性能和安全要求的场景
通过new产生一个对象需要非常繁琐的数据准备或访问权限时,则可以使用原型模式。
3)一个对象多个修改者的场景
一个对象需要提供给其它对象访问而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用。在实际项目中,原型模式很少
单独出现,一般是和工厂方法模式一起出现,通过clone的方法创建一个对象,然后由工厂方法提供给调用者。
四、.NET中原型模式的实现
在.NET中,微软已经为我们提供了原型模式的接口实现,该接口就是ICloneable。其实这个接口就是抽象原型,提供克隆方法,相当于与上面代码中Prototype
抽象类,其中的Clone()方法实现原型模式。如果想自定义的类具有克隆的功能,首先需要在类定义时实现ICloneable接口的Clone方法。
namespace System
{
[ComVisible(true)]
public interface ICloneable
{
object Clone();
}
}
其实在.NET中实现了ICloneable接口的类有很多,如下图所示(只截取了部分,可以用ILSpy反编译工具进行查看):
五、总结
到本篇为止,所有的创建型设计模式就写完了。学习设计模式应该是一个循序渐进的过程,当我们写代码的时候不要一上来就用什么设计模式,而是通过重构
来使用设计模式。
下面总结一下创建型的设计模式:
单例模式解决的是实体对象个数的问题。除了单例模式之外,其它的创建型模式解决的都是new所带来的耦合关系。工厂方法模式、抽象工厂模式、建造者模
式都需要一个额外的工厂类来负责实例化“易变对象”,而原型模式则是通过原型(一个特殊的工厂类把工厂和实体对象耦合在一起了)来克隆“易变对象”。如果
遇到“易变类”,起初的设计通常从工厂方法模式开始,当遇到更多的复杂变化时,再考虑重构为其他三种工厂模式(抽象工厂模式、建造者模式、原型模式)。
一般来说,如果可以使用工厂方法模式,那么一定可以使用原型模式,但是原型模式的使用情况一般是在类比较容易克隆的条件之上。如果是每个类的实现都
比较简单,只需要实现MemberwiseClone而没有引用类型的深拷贝,那么就更加适合了。
C#设计模式学习笔记:(5)原型模式的更多相关文章
- 设计模式学习笔记——Prototype原型模式
原型模型就是克隆. 还有深克隆.浅克隆,一切听上去都那么耳熟能详.
- 设计模式学习系列6 原型模式(prototype)
原型模式(prototype)用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象.允许一个对象再创建另外一个新对象的时候根本无需知道任何创建细节,只需要请求圆形对象的copy函数皆可. 1 ...
- 设计模式学习笔记--备忘录(Mamento)模式
写在模式学习之前 什么是设计模式:在我们进行程序设计时,逐渐形成了一些典型问题和问题的解决方式,这就是软件模式:每个模式描写叙述了一个在我们程序设计中常常发生的问题,以及该问题的解决方式:当我们碰到模 ...
- java学习笔记之原型模式及深浅拷贝
一.原型模式的基本介绍 在聊原型模式之前,我们来思考一个小问题,传统的方式我们是如何克隆对象呢? 那我们以多利羊(Sheep)为例,来说明上述这个问题,具体代码见下面: 多利羊(Sheep) publ ...
- 设计模式学习笔记-Adapter模式
Adapter模式,就是适配器模式,使两个原本没有关联的类结合一起使用. 平时我们会经常碰到这样的情况,有了两个现成的类,它们之间没有什么联系,但是我们现在既想用其中一个类的方法,同时也想用另外一个类 ...
- Java-马士兵设计模式学习笔记-装饰者模式
Java装饰者模式简介 一.假设有一个Worker接口,它有一个doSomething方法,Plumber和Carpenter都实现了Worker接口,代码及关系如下: 1.Worker.java p ...
- 研磨设计模式学习笔记2--外观模式Facade
需求:客户端需要按照需求,执行一个操作,操作包括一个系统中的3个模块(根据配置选择是否全部执行). 外观模式优点: 客户端无需知道系统内部实现,,只需要写好配置文件,控制那些模块执行,简单易用. 外观 ...
- 设计模式学习笔记 1.factory 模式
Factory 模式 用户不关心工厂的具体类型,只知道这是一个工厂就行. 通过工厂的实现推迟到子类里面去来确定工厂的具体类型. 工厂的具体类型来确定生产的具体产品. 同时用户不关心这是一个什么样子的产 ...
- 设计模式学习笔记——Composite 组合模式
用于描述无限层级的复杂对象,类似于描述资源管理器,抽象出每一个层级的共同特点(文件夹和文件,展开事件) 以前描述一个对象,是将整个对象的全部数据都描述清楚,而组合模式通过在对象中定义自己,描述自己的下 ...
- 设计模式学习笔记——Bridge 桥接模式
先说一下我以前对桥接模式的理解:当每个类中都使用到了同样的属性或方法时,应该将他们单独抽象出来,变成这些类的属性和方法(避免重复造轮子),当时的感觉是和三层模型中的model有点单相似,也就是让mod ...
随机推荐
- testng使用详解
一.testng 介绍 TestNG 是一个测试框架,其灵感来自 JUnit 和 NUnit,但同时引入了一些新的功能,使其功能更强大,使用更方便. TestNG 设计涵盖所有类型的测试:单元,功能, ...
- React 解析/ 第二节 使用 Reac
官方脚手架 create-react-app React 提供了一个官方的命令行工具(CLI)—— create-react-app,是专门用于快速搭建单页面应用(SPA)的脚手架,它基于 Webpa ...
- echats 的使用
第一步在我们的电脑上百度搜索echarts,点击进去,如下图所示: 2 第二步进去之后,点击下载,选择要下载的echarts版本,一般选择源代码,如下图所示: 3 第三步下载完成之后,我们也可以来使用 ...
- 自定义BeanDefinitionRegistryPostProcessor
自定义BeanDefinitionRegistryPostProcessor 概述 BeanDefinitionRegistryPostProcessor继承自BeanFactoryPostProce ...
- composer intall 报错
报错 [Composer\Exception\NoSslException] The openssl extension is required for SSL/TLS protection but ...
- Maven的scope属性作用域范围
在POM 4中,<dependency>中还引入了<scope>,它主要管理依赖的部署.目前<scope>可以使用5个值: 1. compile,缺省值,适用于所有 ...
- logback 发送邮件和自定义发送邮件;java类发送邮件
使用logback发送邮件 需求: 1.报错发邮件,定位错误位置以尽快解决:(报错发送邮件) 2.某一项重要操作完成之后发送邮件:(自定义发送邮件) 没有接触过logback,怎么办? 没办法,硬着头 ...
- springboot打印sql语句及执行时间
有时候我们程序的接口比较耗时,需要优化,这时我们可能需要了解该接口执行了哪些sql语句以及耗时 1.引入jar包 <!--监控sql日志--> <dependency> < ...
- 看完这篇文章,再次遇到Jedis「Redis客户端」异常相信你不再怕了!
本文导读: [1] 疫情当前 [2] 应用异常监控 [3] Redis客户端异常分析 [4] Redis客户端问题引导分析 [5] 站在Redis客户端视角分析 [6] 站在Redis服务端视角分析 ...
- 使用 TF-IDF 加权的空间向量模型实现句子相似度计算
使用 TF-IDF 加权的空间向量模型实现句子相似度计算 字符匹配层次计算句子相似度 计算两个句子相似度的算法有很多种,但是对于从未了解过这方面算法的人来说,可能最容易想到的就是使用字符串匹配相关的算 ...