大话设计模式之五:6~10章(装饰模式 、代理模式、工厂方法模式、原型模式、模板方法模式)
注:《大话设计模式》这本书很好的介绍了设计模式,其对应的源码是C#语言写得,跑在visual studio上,所以自己先安装visual studio ,然后将源码跑一跑,这样能深刻的理解《大话设计模式这本书》,现在将整个过程整理好,方便别人也方便自己!
第六章:穿什么有这么重要?——装饰模式
P50
优点:
第一:使书写的代码具有柔韧性,能够以一系列的功能来增加代替对象的行为,它不会污染原有的代码,反而会让代码容易编写,容易对外扩展。
第二:它将每个功能放在一个单独的类中,让这个类去包装要装饰的对象,这样就将装饰功能类与原有的类分开,简化了原有类,类的核心职责和装饰功能也区分开了,而且相关类中重复的装饰逻辑也没有了。
缺点:
第一:如果原有的对象的接口发生变化了,那么他所有的装饰类也得修改,进而匹配接口。
第二:如果装饰链过大,系统就会花费较长的时间去用于初始化对象,影响信息的传递。
适用范围:
第一:想在不影响其他类的情况下,动态的给对象添加新的职责方法;
第二:给对象增加职责可能会在未来发生改变;
第三:用子类来扩展功能不方便的情况下
1.初识代理模式
生活中大家一定遇到这样的情况了:比如说我要买一条毛巾,不妨就是洁丽雅的吧,那一般人的话我应该不是去洁丽雅的工厂里面直接去买吧,而是我们在洁丽雅专卖店或者什么超市啊,这些地方购买,这些地方实际上就是洁丽雅毛巾的代理。这其实和我们OO中的代理模式是很相似的。
一个it人士,上网应该是经常的事了吧,那么总会有这样的情况,打开一个网页,文字先出现,而那些比较大的资源,例如图片要等等才会显示出来,这是为什么呢??实际上这其中采用的就是代理模式。
2.代理模式的一个简单的例子
就说上面的毛巾的例子吧,将有如下类图:
消费者虽然买的是洁丽雅工厂生产的毛巾,确实通过洁丽雅专卖店买到的毛巾,并不是直接去厂家拿货,这就是代理模式。看看代码吧:
1 //定义了接口 2 interface SaleTowelIntertace 3 { 4 void sellTowel(); 5 } 6 //毛巾的生产类 7 class TowelProduce : SaleTowelIntertace 8 { 9 string ConsumerName; 10 TowelProduce(string consumer_name) 11 { 12 ConsumerName = consumer_name; 13 } 14 void sellTowel() 15 { 16 Console.WriteLine("毛巾卖给了{0}",ConsumerName); 17 } 18 } 19 //卖毛巾的代理类 20 class TowelSaleProxy : SaleTowelIntertace 21 { 22 TowelProduce tp; 23 TowelSaleProxy(string consumner_name) 24 { 25 tp = new TowelProduce(consumer_name); 26 } 27 void sellTowel() 28 { 29 tp.sellTowel(); 30 } 31 }
看了这段代码,可能会想,那么这个代理有什么用呢?似乎看不到具体有什么好处啊!!其实这样的主要好处是真正购买毛巾的客户和厂家并不存在联系,即减小了他们之间的耦合性。这样说似乎还是有点抽象了,而这又是一个关键的问题。所以……
3.代理模式的意义
代理模式到底好处在哪里呢??
那先要说一下代理模式中的三种角色了。
抽象角色:声明真实对象和代理对象的共同接口。
代理角色:代理对象角色内部含有对真实对象的引用,从而可以操作真实对象,同时代理对象提供与真实对象相同的接口以便在任何时刻都能代替真实对象。同时,代理对象 可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装。
真实角色:代理角色所代表的真实对象,是我们最终要引用的对象。
代理模式的一个好处就是对外部提供统一的接口方法,而代理类在接口中实现对真实类的附加操作行为,从而可以在不影响外部调用情况下,进行系统扩展。也就是说,我要修改真实角色的操作的时候,尽量不要修改他,而是在外部在“包”一层进行附加行为,即代理类。例如:接口A有一个接口方法operator(),真实角色:RealA实现接口A,则必须实现接口方法operator()。客户端Client调用接口A的接方法operator()。现在新需求来了,需要修改RealA中的operator()的操作行为。怎么办呢?如果修改RealA就会影响原有系统的稳定性,还要重新测试。这是就需要代理类实现附加行为操作。创建代理ProxyA实现接口A,并将真实对象RealA注入进来。ProxyA实现接口方法operator(),另外还可以增加附加行为,然后调用真实对象的operator()。从而达到了“对修改关闭,对扩展开放”,保证了系统的稳定性。我们看客户端Client调用仍是接口A的接口方法operator(),只不过实例变为了ProxyA类了而已。也就是说代理模式实现了ocp原则。
4.什么时候使用代理模式
当我们需要使用的对象很复杂或者需要很长时间去构造,这时就可以使用代理模式(Proxy)。例如:如果构建一个对象很耗费时间和计算机资源,代理模式(Proxy)允许我们控制这种情况,直到我们需要使用实际的对象。一个代理(Proxy)通常包含和将要使用的对象同样的方法,一旦开始使用这个对象,这些方法将通过代理(Proxy)传递给实际的对象。 一些可以使用代理模式(Proxy)的情况:
一个对象,比如一幅很大的图像,需要载入的时间很长。
一个需要很长时间才可以完成的计算结果,并且需要在它计算过程中显示中间结果
一个存在于远程计算机上的对象,需要通过网络载入这个远程对象则需要很长时间,特别是在网络传输高峰期。
一个对象只有有限的访问权限,代理模式(Proxy)可以验证用户的权限
代理模式(Proxy)也可以被用来区别一个对象实例的请求和实际的访问,例如:在程序初始化过程中可能建立多个对象,但并不都是马上使用,代理模式(Proxy)可以载入需要的真正的对象。这是一个需要载入和显示一幅很大的图像的程序,当程序启动时,就必须确定要显示的图像,但是实际的图像只能在完全载入后才可以显示!这时我们就可以使用代理模式(Proxy)。
例子1:孙悟空拔下一嘬猴毛,轻轻一吹就会变出好多的孙悟空来。
例子2:寄个快递
下面是一个邮寄快递的场景:
“给我寄个快递。”顾客说。
“寄往什么地方?寄给……?”你问。
“和上次差不多一样,只是邮寄给另外一个地址,这里是邮寄地址……”顾客一边说一边把写有邮寄地址的纸条给你。
“好!”你愉快地答应,因为你保存了用户的以前邮寄信息,只要复制这些数据,然后通过简单的修改就可以快速地创建新的快递数据了
浅拷贝和深拷贝
原型模式的原理图:浅拷贝
被拷贝对象的所有变量都含有与原对象相同的值,而且对其他对象的引用仍然是指向原来的对象。即浅拷贝只负责当前对象实例,对引用的对象不做拷贝。浅复制后的对象和对象副本的情况:
深拷贝
被拷贝对象的所有的变量都含有与原来对象相同的值,除了那些引用其他对象的变量。那些引用其他对象的变量将指向一个被拷贝的新对象,而不再是原有那些被引用对象。即 深拷贝把要拷贝的对象所引用的对象也都拷贝了一次,而这种对被引用到的对象拷贝叫做间接拷贝。深复制的对象和对象副本的情况:深拷贝要深入到多少层,是一个不确定的问题。
在决定以深拷贝的方式拷贝一个对象的时候,必须决定对间接拷贝的对象是采取浅拷贝还是深拷贝还是继续采用深拷贝。
因此,在采取深拷贝时,需要决定多深才算深。此外,在深拷贝的过程中,很可能会出现循环引用的问题。