本笔记摘抄自:http://www.javashuo.com/article/p-ktjdqwpc-gh.html,记录一下学习过程以备后续查用。html
1、引言设计模式
不少人说原型设计模式会节省机器内存,他们说是拷贝出来的对象是原型的复制,不会使用内存。我认为这是不对的,由于拷贝出来的每个对象都是实际缓存
存在的,每一个对象都有本身独立的内存地址且会被GC回收。若是就浅拷贝来讲,可能会公用一些字段(引用类型),但深拷贝是不会的。因此说原型设计模式会安全
提升内存使用率是不必定的,具体还要看当时的设计,若是拷贝出来的对象缓存了,每次使用的是缓存的拷贝对象,那就另当别论,再说该模式自己解决的不ide
是内存使用率的问题。工具
附:浅复制与深复制的区别
浅复制一个对象:
1)若是这个对象(如int age=18 )是值类型,则获得的对象是一个全新的值类型对象(新的内存地址);
2)若是这个对象是引用类型(如class Person):
I、这个对象中的值类型(如person.Age=18)是一个全新的值类型对象(新的内存地址);
II、这个对象中的引用类型是公用的(同一内存地址),当原始引用类型的值变化时,新生成对象的引用类型的值也会跟着变化。
深复制一个对象:
不管以前这个对象是值类型仍是引用类型,获得的新对象都是一个全新的对象(新的内存地址)。
在软件系统中,当建立一个类的实例的过程很昂贵或很复杂,而且咱们须要建立多个这样的类的实例时,若是用new操做符去建立时,会增长建立的复杂度性能
与客户代码的耦合度。若是采用工厂方法模式来建立这样的实例对象的话,随着产品类的不断增长,致使子类的数量不断增多,也致使了相应工厂类的增长,学习
系统复杂程度随之增长,因此此时使用工厂方法模式来封装类的建立过程并不合适。优化
因为每一个类的实例都是相同的(这个相同指的是类型相同,可是每一个实例的状态参数会有不一样,若是状态数值也相同就没意义了),有一个这样的对象就可spa
以了。当咱们须要多个相同的类实例时,能够经过对原来对象拷贝一份来完成建立,这个思路正是原型模式的实现方式。
2、原型模式介绍
原型模式:英文名称--Prototype Pattern;分类--建立型。
2.一、动机(Motivate)
在软件系统中,常常面临着“某些结构复杂的对象”的建立工做,因为需求的变化,这些对象常常面临着剧烈的变化,可是它们却拥有比较稳定一致的接口。
如何应对这种变化?如何向“客户程序(使用这些对象的程序)”隔离出“这些易变对象”,从而使得“依赖这些易变对象的客户程序”不随着需求改变而改变?
2.二、意图(Intent)
使用原型实例指定建立对象的种类,而后经过拷贝这些原型来建立新的对象。--《设计模式》Gof
2.三、结构图(Structure)
2.四、模式的组成
从上图能够看出,在原型模式的结构图有如下角色:
1)原型类(Prototype):原型类,声明一个Clone自身的接口。
2)具体原型类(ConcretePrototype):实现一个Clone自身的操做。
在原型模式中,Prototype一般提供一个包含Clone方法的接口,具体的原型ConcretePrototype使用Clone方法完成对象的建立。
2.五、原型模式的具体实现
《大话西游之大圣娶亲》这部电影,里面有这样一个场景:牛魔王使用无敌牛虱大战至尊宝,至尊宝的应对之策就是--从脑后拔下一撮猴毛,吹了口仙气,
无数猴子猴孙现身来大战牛魔王的无敌牛虱。至尊宝的猴子猴孙就是该原型模式的最好体现,至尊宝建立本身的一个副本,不用还要从新孕育五百年,而后出
世、再学艺,最后再来和老牛大战,假如这样的话,估计黄花菜都凉了。至尊宝有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 } }
运行结果以下:
3、原型模式的实现要点
Prototype模式一样用于隔离类对象的使用者和具体类型(易变类)之间的耦合关系,它一样要求这些“易变类”拥有“稳定的接口”。
Prototype模式对于“如何建立易变类的实体对象”(建立型模式除了Singleton模式之外,都是用于解决建立易变类的实体对象的问题的)采用“原型克隆”的方
法来作,它使得咱们能够很是灵活地动态建立“拥有某些稳定接口”的新对象——所需工做仅仅是注册一个新类的对象(即原型),而后在任何须要的地方不断
地Clone。
Prototype模式中的Clone方法能够利用.NET中的Object类的MemberwiseClone()方法或者序列化来实现深拷贝。
3.一、原型模式的优势
1)原型模式向客户隐藏了建立新实例的复杂性。
2)原型模式容许动态增长或较少产品类。
3)原型模式简化了实例的建立结构,工厂方法模式须要有一个与产品类等级结构相同的等级结构,而原型模式不须要这样。
4)产品类不须要事先肯定产品的等级结构,由于原型模式适用于任何的等级结构。
3.二、原型模式的缺点
1)每一个类必须配备一个克隆方法。
2)配备克隆方法须要对类的功能进行通盘考虑,这对于全新的类不是很难,但对于已有的类不必定很容易,特别当一个类引用不支持串行化的间接对象,或
者引用含有循环结构的时候。
3.三、原型模式使用的场景
1)资源优化场景
类初始化须要消化很是多的资源,这个资源包括数据、硬件资源等。
2)性能和安全要求的场景
经过new产生一个对象须要很是繁琐的数据准备或访问权限时,则可使用原型模式。
3)一个对象多个修改者的场景
一个对象须要提供给其它对象访问并且各个调用者可能都须要修改其值时,能够考虑使用原型模式拷贝多个对象供调用者使用。在实际项目中,原型模式不多
单独出现,通常是和工厂方法模式一块儿出现,经过clone的方法建立一个对象,而后由工厂方法提供给调用者。
4、.NET中原型模式的实现
在.NET中,微软已经为咱们提供了原型模式的接口实现,该接口就是ICloneable。其实这个接口就是抽象原型,提供克隆方法,至关于与上面代码中Prototype
抽象类,其中的Clone()方法实现原型模式。若是想自定义的类具备克隆的功能,首先须要在类定义时实现ICloneable接口的Clone方法。
namespace System { [ComVisible(true)] public interface ICloneable { object Clone(); } }
其实在.NET中实现了ICloneable接口的类有不少,以下图所示(只截取了部分,能够用ILSpy反编译工具进行查看):
5、总结
到本篇为止,全部的建立型设计模式就写完了。学习设计模式应该是一个按部就班的过程,当咱们写代码的时候不要一上来就用什么设计模式,而是经过重构
来使用设计模式。
下面总结一下建立型的设计模式:
单例模式解决的是实体对象个数的问题。除了单例模式以外,其它的建立型模式解决的都是new所带来的耦合关系。工厂方法模式、抽象工厂模式、建造者模
式都须要一个额外的工厂类来负责实例化“易变对象”,而原型模式则是经过原型(一个特殊的工厂类把工厂和实体对象耦合在一块儿了)来克隆“易变对象”。若是
遇到“易变类”,起初的设计一般从工厂方法模式开始,当遇到更多的复杂变化时,再考虑重构为其余三种工厂模式(抽象工厂模式、建造者模式、原型模式)。
通常来讲,若是可使用工厂方法模式,那么必定可使用原型模式,可是原型模式的使用状况通常是在类比较容易克隆的条件之上。若是是每一个类的实现都
比较简单,只须要实现MemberwiseClone而没有引用类型的深拷贝,那么就更加适合了。