1、引言
在开始今天的文章以前先说明一点,欢迎你们来指正。不少人说原型设计模式会节省机器内存,他们说是拷贝出来的对象,这些对象其实都是原型的复制,不会使用内存。我认为这是不对的,由于拷贝出来的每个对象都是实际存在的,每一个对象都有本身的独立内存地址,都会被GC回收。若是就浅拷贝来讲,可能会公用一些字段,深拷贝是不会的,因此说原型设计模式会提升内存使用率,不必定。具体还要看当时的设计,若是拷贝出来的对象缓存了,每次使用的是缓存的拷贝对象,那就另当别论了,再说该模式自己解决的不是内存使用率的问题。
如今说说原型模式的要解决的问题吧,在软件系统中,当建立一个类的实例的过程很昂贵或很复杂,而且咱们须要建立多个这样类的实例时,若是咱们用new操做符去建立这样的类实例,这就会增长建立类的复杂度和建立过程与客户代码复杂的耦合度。若是采用工厂模式来建立这样的实例对象的话,随着产品类的不断增长,致使子类的数量不断增多,也致使了相应工厂类的增长,维护的代码维度增长了,由于有产品和工厂两个维度了,反而增长了系统复杂程度,因此在这里使用工厂模式来封装类建立过程并不合适。因为每一个类实例都是相同的,这个相同指的是类型相同,可是每一个实例的状态参数会有不一样,若是状态数值也相同就没意义了,有一个这样的对象就能够了。当咱们须要多个相同的类实例时,能够经过对原来对象拷贝一份来完成建立,这个思路正是原型模式的实现方式。
2、原型模式的详细介绍
2.一、动机(Motivate)
在软件系统中,常常面临着“某些结构复杂的对象”的建立工做;因为需求的变化,这些对象常常面临着剧烈的变化,可是它们却拥有比较稳定一致的接口。如何应对这种变化?如何向“客户程序(使用这些对象的程序)”隔离出“这些易变对象”,从而使得“依赖这些易变对象的客户程序”不随着需求改变而改变?
2.二、意图(Intent)
使用原型实例指定建立对象的种类,而后经过拷贝这些原型来建立新的对象。 --《设计模式》Gof
2.三、结构图(Structure)
2.四、模式的组成
能够看出,在原型模式的结构图有如下角色:
(1)、原型类(Prototype):原型类,声明一个Clone自身的接口;
(2)、具体原型类(ConcretePrototype):实现一个Clone自身的操做。
在原型模式中,Prototype一般提供一个包含Clone方法的接口,具体的原型ConcretePrototype使用Clone方法完成对象的建立。
2.5 原型模式的具体实现
《大话西游之大圣娶亲》这部电影,没看过的人很少吧,里面有这样一个场景。牛魔王使用无敌牛虱大战至尊宝,至尊宝的应对之策就是,从脑后把下一撮猴毛,吹了口仙气,无数猴子猴孙现身,来大战牛魔王的无敌牛虱。至尊宝的猴子猴孙就是该原型模式的最好体现。至尊宝建立本身的一个副本,不用还要从新孕育五百年,而后出世,再学艺,最后来和老牛大战,估计黄花菜都凉了。他有3根救命猴毛,轻轻一吹,想要多少个本身就有多少个,方便,快捷。
设计模式
1 /// <summary> 2 /// 原型设计模式,每一个具体原型是一类对象的原始对象,经过每一个原型对象克隆出来的对象也能够进行设置,在原型的基础之上丰富克隆出来的对象,因此要设计好抽象原型的接口 3 /// </summary> 4 namespace 设计模式之原型模式 5 { 6 /// <summary> 7 /// 客户类 8 /// </summary> 9 class Customer 10 { 11 static void Main(string[] args) 12 { 13 Prototype xingZheSun = new XingZheSunPrototype(); 14 Prototype xingZheSun2 = xingZheSun.Clone(); 15 Prototype xingZheSun3 = xingZheSun.Clone(); 16 17 Prototype sunXingZhe = new SunXingZhePrototype(); 18 Prototype sunXingZhe2 = sunXingZhe.Clone(); 19 Prototype sunXingZhe3 = sunXingZhe.Clone(); 20 Prototype sunXingZhe4 = sunXingZhe.Clone(); 21 Prototype sunXingZhe5 = sunXingZhe.Clone(); 22 23 //1号孙行者打妖怪 24 sunXingZhe.Fight(); 25 //2号孙行者去化缘 26 sunXingZhe2.BegAlms(); 27 28 //战斗和化缘也能够分类,好比化缘,能够分:水果类化缘,饭食类化缘;战斗能够分为:天界宠物下界成妖的战斗,天然修炼成妖的战斗,你们能够本身去想吧,原型模式仍是颇有用的 29 30 Console.Read(); 31 } 32 } 33 34 /// <summary> 35 /// 抽象原型,定义了原型自己所具备特征和动做,该类型就是至尊宝 36 /// </summary> 37 public abstract class Prototype 38 { 39 // 战斗--保护师傅 40 public abstract void Fight(); 41 // 化缘--不要饿着师傅 42 public abstract void BegAlms(); 43 44 // 吹口仙气--变化一个本身出来 45 public abstract Prototype Clone(); 46 } 47 48 /// <summary> 49 /// 具体原型,例如:行者孙,他只负责化斋饭食和与天界宠物下界的妖怪的战斗 50 /// </summary> 51 public sealed class XingZheSunPrototype:Prototype 52 { 53 // 战斗--保护师傅--与天然修炼成妖的战斗 54 public override void Fight() 55 { 56 Console.WriteLine("腾云驾雾,各类武艺"); 57 } 58 // 化缘--不要饿着师傅--饭食类 59 public override void BegAlms() 60 { 61 Console.WriteLine("什么都能要来"); 62 } 63 64 // 吹口仙气--变化一个本身出来 65 public override Prototype Clone() 66 { 67 return (XingZheSunPrototype)this.MemberwiseClone(); 68 } 69 } 70 71 /// <summary> 72 /// 具体原型,例如:孙行者,他只负责与天然界修炼成妖的战斗和化斋水果 73 /// </summary> 74 public sealed class SunXingZhePrototype : Prototype 75 { 76 // 战斗--保护师傅-与天界宠物战斗 77 public override void Fight() 78 { 79 Console.WriteLine("腾云驾雾,各类武艺"); 80 } 81 // 化缘--不要饿着师傅---水果类 82 public override void BegAlms() 83 { 84 Console.WriteLine("什么都能要来"); 85 } 86 87 // 吹口仙气--变化一个本身出来 88 public override Prototype Clone() 89 { 90 return (SunXingZhePrototype)this.MemberwiseClone(); 91 } 92 } 93 }
上面代码中都有详细的注释代码,这里就不过多解释。
3、原型模式的实现要点:
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的方法建立一个对象,而后由工厂方法提供给调用者。
4、.NET 中原型模式的实现
在.NET中,微软已经为咱们提供了原型模式的接口实现,该接口就是ICloneable,其实这个接口就是抽象原型,提供克隆方法,至关于与上面代码中Prototype抽象类,其中的Clone()方法来实现原型模式,若是咱们想咱们自定义的类具备克隆的功能,首先定义类实现ICloneable接口的Clone方法。其实在.NET中实现了ICloneable接口的类有不少,以下图所示(图中只截取了部分,能够用ILSpy反编译工具进行查看):缓存
namespace System { [ComVisible(true)] public interface ICloneable { object Clone(); } }
在Net的FCL里面实现ICloneable接口的类如图,本身能够去查看每一个类本身的实现,在此就不贴出来了。安全
5、总结
到今天为止,全部的建立型设计模式就写完了。学习设计模式应该是有一个按部就班的过程,当咱们写代码的时候不要一上来就用什么设计模式,而是经过重构来使用设计模式。建立型的设计模式写完了,咱们就总结一下。Singleton单件模式解决的是实体对象个数的问题。除了Singleton以外,其余建立型模式解决的都是new所带来的耦合关系。 Factory Method,Abstract Factory,Builder都须要一个额外的工厂类来负责实例化“易变对象”,而Prototype则是经过原型(一个特殊的工厂类)来克隆“易变对象”。(其实原型就是一个特殊的工厂类,它只是把工厂和实体对象耦合在一块儿了)。若是遇到“易变类”,起初的设计一般从Factory Method开始,当遇到更多的复杂变化时,再考虑重构为其余三种工厂模式(Abstract Factory,Builder,Prototype)。
通常来讲,若是可使用Factory Method,那么必定可使用Prototype。可是Prototype的使用状况通常是在类比较容易克隆的条件之上,若是是每一个类实现比较简单,均可以只用实现MemberwiseClone,没有引用类型的深拷贝,那么就更适合了。ide