装饰模式(Decorator Pattern)
概述:
在软件开发中我们常常遇到这样的问题,在设计一个类后,发现这个类的某个方法,满足不了我们的需要,需要为他新增一个功能,比如在项目中遇到一个磁盘读写业务,要求可以设计一个类,正常读写磁盘内容,我们很自然的写设计一个类Disk 提供2个方法write和reader,结果实现后客户说我在写入的时候需要记录我写入的这个文件的大小。程序员通常是再写一个类 ChildDisk 集成自Disk类,然后重写write的方法,可不可以呢?可以!但我们要知道继承属于类型引入的静态方式,使得扩展缺乏足够的灵活性;并且随着用户需求的不断变化增多,加上子类的组合会导致类爆炸。当你完成了重写write方法后客户说需要写入后再在其他地方备份一个同样的文件。。。。好!你发现这个业务只需要继承ChildDisk 类再重现写write方法 起名为,ChildDisk1。过了些天用户说读取磁盘的时候我需要记录用户信息读取时间等等。。。。估计你快疯了,继承了那么多的类,你已经快要忘记咯他们的作用。这时我们迫切的需要怎么能动态的扩展类行为从而把继承带来的影响降到最低?装饰模式就可以解决你这个问题。通过装饰模式我们怎么来解决这个问题呢?看下图:
<shapetype id="_x0000_t75" stroked="f" filled="f" path="m@4@5l@4@11@9@11@9@5xe" o:preferrelative="t" o:spt="75" coordsize="21600,21600"><stroke joinstyle="miter"></stroke><formulas><f eqn="if lineDrawn pixelLineWidth 0"></f><f eqn="sum @0 1 0"></f><f eqn="sum 0 0 @1"></f><f eqn="prod @2 1 2"></f><f eqn="prod @3 21600 pixelWidth"></f><f eqn="prod @3 21600 pixelHeight"></f><f eqn="sum @0 0 1"></f><f eqn="prod @6 1 2"></f><f eqn="prod @7 21600 pixelWidth"></f><f eqn="sum @8 21600 0"></f><f eqn="prod @7 21600 pixelHeight"></f><f eqn="sum @10 21600 0"></f></formulas><path o:connecttype="rect" gradientshapeok="t" o:extrusionok="f"></path><lock aspectratio="t" v:ext="edit"></lock></shapetype><shape id="_x0000_i1026" style="WIDTH: 415.5pt; HEIGHT: 333.75pt" type="#_x0000_t75"><imagedata o:title="" src="file:///C:%5CDOCUME~1%5CFANWEI~1%5CLOCALS~1%5CTemp%5Cmsohtml1%5C01%5Cclip_image001.emz"></imagedata></shape>
解释:
1. 定义一个IDiskHander接口创建Write和Reader方法
2. 定义一个Disk磁盘的抽象类,继承IDiskHander 接口
3. 定义三星的移动硬盘和希捷的硬盘继承自Disk,分别实现原始的Write和Reader方法
4. 创建磁盘包装器抽象类DiskWrapper继承自Disk
5. 创建磁盘写操作时额外的职责类(GetFileLength功能)WriteDisk
6. 创建磁盘读操作时额外的职责类(BackUpFile功能)ReaderDisk
这样一来不管用户再提什么BT的新要求,我们只要再创建一个类继承自DiskWrapper 这样我可以不改变原有业务的情况下动态的分配额外职责可增可减,就像插件一样,用到了这个功能时就插上去,不用了就再拔下来。来看看他的客户端的代码把,如此简单:
XiJieDisk xj = new XiJieDisk();//创建希捷硬盘
WriteDisk md = new WriteDisk(xj);//新插一个写业务
ReaderDisk hd = new ReaderDisk(md);//将写业务和新的读业务整合到一起
hd.Reader();
hd.Write();
效果:
<shape id="_x0000_i1027" style="WIDTH: 414.75pt; HEIGHT: 272.25pt" type="#_x0000_t75"><imagedata o:title="" src="file:///C:%5CDOCUME~1%5CFANWEI~1%5CLOCALS~1%5CTemp%5Cmsohtml1%5C01%5Cclip_image003.png"></imagedata></shape>
OK我们先再去掉写业务再来看看
代码:
XiJieDisk xj = new XiJieDisk();
ReaderDisk hd = new ReaderDisk(xj);
hd.Reader();
hd.Write();
效果图:
<shape id="_x0000_i1028" style="WIDTH: 414.75pt; HEIGHT: 272.25pt" type="#_x0000_t75"><imagedata o:title="" src="file:///C:%5CDOCUME~1%5CFANWEI~1%5CLOCALS~1%5CTemp%5Cmsohtml1%5C01%5Cclip_image005.png"></imagedata></shape>
OOD设计合理性:
1. 是否符合开不原则:
在则从添加时我们只需要再增加一个继承自DiskWrapper类的子类,不需要重现Disk的读写方法,所以对类修改关闭,对外增加开放
符合
2. 是否符合里氏代换:
所有额外职责都依赖于DiskWrapper抽象类,我们在替换具体额外职责类时是很方便的
符合
3. 是否符合抽象原则
符合
4. 是否符合迪米特法则
Disk类只用一个IDiskHander操作接口
符合
装饰模式总结
意图:
动态地给一个对象添加一些额外的职责(功能)别名包装器(Wrapper)
结构图:
<shape id="_x0000_i1025" style="WIDTH: 323.25pt; HEIGHT: 267pt" type="#_x0000_t75" alt=""><imagedata src="file:///C:%5CDOCUME~1%5CFANWEI~1%5CLOCALS~1%5CTemp%5Cmsohtml1%5C01%5Cclip_image007.gif" o:href="http://terrylee.cnblogs.com/images/cnblogs_com/terrylee/Decorator_02.gif"></imagedata></shape>
使用背静:
1. 在不影响其他对象的情况下,以动态、透明的方式给单个对性添加职责
2. 吃力那些可以撤销的职责
3. 当不能采用生成子类的方法进行扩充时。
模式优缺点:
优点:
1. 比静态继承更灵活
2. 避免在层次结构高层类有太多的特征
3. 设计师可以根据这些小职责类组合成功能丰富的类行为
缺点:
1. 小类太多难以维护
注意:
1. 接口的一致性:装饰对象的接口必须于被装饰的类接口保持一致。
2. 如果只是为一个类添加单一的简单职责可以省去创建包装器类的过程(这里指的是DiskWrapper)
3. 尽量保持Component类的简单性(这里指的是Disk):如果Component过于复杂可能导致在创建额外职责是带来大量的工作
4. Decorator模式是改变对象外壳模式,他不会对类的具体内核业务有所改动。随便提一句下面行为模式中要讲的策略模式是改变类对象内核的
代码:下载