装饰模式
装饰模式动态的将责任附加到对象上。就增加功能来说,装饰模式比生成子类更为灵活。装饰模式在GOF95中分类为结构模式。

- 装饰者和被装饰对象有相同的超类型。这里使用继承的目的是类型匹配,而不是为了通过继承获得行为
 - 可以用一个或多个装饰者包装一个对象(多层包装),对象可以在运行时被动态装饰
 - 由于装饰器与被装饰对象就有相同的超类型,所有在任何需要原始对象的场合,都可以使用装饰对象代替之(里氏替换原则)
 - 装饰器可以在被装饰对象的行为之前或(和)之后,加上自己的行为,甚至完全替换被装饰对象的行为,以达到特定目的。这是装饰模式的关键点,这些新的行为是通过组合得到的
 
装饰模式具有以下优点:
- 比继承灵活:在运行时动态装饰,且可以多层包装
 - 更容易复用:通过将功能分散到简单的、功能相对单一的装饰器中,通过层层包装复用
 
装饰模式具有以下缺点:
- 被装饰的内层对象,其真实类型被隐藏,因此客户端难以对某种具体类型做特定的操作
 - 装饰模式常常导致设计中出现大量的小类,对客户端程序员造成困扰
 - 不同装饰器之间不能有关联性
 
考虑一个煎饼果子摊位,根据食材的不同进行收费:绿豆面饼皮、杂粮饼皮、玉米饼皮、荞麦饼皮,这些是主料,食客只能选择一种;鸡蛋、鸭蛋、火腿肠、培根、馃子、馃篦、雪菜、葱花、蒜泥、榨菜……等等这些是辅料,可以任意组合,制作成各式各样的煎饼果子。这样的需求如果使用继承来实现,将导致“类爆炸”的发生,而装饰模式却可以很轻松的满足需要:
| 
					 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88  | 
						public class TianjinHamburgerApplication {     //食材接口     static interface Material{         //计算价格         int getCost();     }     //面皮     abstract static class Sheet implements Material{     }     //绿豆面皮     static class GreenBeanSheet extends Sheet{         public int getCost(){             return 2;         }     }     //玉米面皮     static class CornSheet extends Sheet{         public int getCost(){             return 1;         }     }     //辅材,可以任意搭配,以满足不同口味需求     static abstract class Side implements Material{         private Material material;         public Side(Material material){             this.material = material;         }         public int getCost()         {             return material.getCost();         }     }      //鸡蛋     static class Egg extends Side{         private int num;         public Egg( int num, Material material ){             super(material);             this.num = num;         }         @Override         public int getCost()         {             return super.getCost() + this.num * 1;         }     }     //油条     static class FriedStick extends Side{         public FriedStick( Material material )         {             super( material );         }         public int getCost()         {             return super.getCost() + 2;         }     }     //雪菜     static class PickledMustard  extends Side{         public PickledMustard( Material material )         {             super( material );         }         public int getCost()         {             return super.getCost() + 1;         }     }     public static void main( String[] args )     {         //给我来个煎饼果子         Material tianjinHamburger = new GreenBeanSheet();         //来根油条         tianjinHamburger = new FriedStick( tianjinHamburger );         //加十个鸡蛋         tianjinHamburger =  new Egg( 10,tianjinHamburger );         //加点雪菜啊         tianjinHamburger = new PickledMustard( tianjinHamburger );         //结账         System.out.println(tianjinHamburger.getCost());     } }  | 
					
在Java I/O框架中,装饰模式被广泛的使用,以提供缓冲、回退、按行处理等功能,下面是Java I/O框架输入流的部分类图:

其中FilterInputStream是一个抽象的装饰器,它具有InputStream接口,它的子类用来装饰基础的输入流,提供各种功能。除了InputStream以外,OutputStream、Writer、Reader采用了类似的设计。
AOP是一种编程范式,它从另外一个角度考虑程序结构以完善OO。在传统的OO开发中,都是从“纵向”角度分析系统,因此系统架构图都是自上而下,层层依赖。系统的每个业务模块常常分为表现层、逻辑层、数据层。在设计过程中会发现,不同的模块会存在一些公用的功能,例如日志管理、事物管理、安全性检查等。这时候,就应当改为“横向”角度来分析系统,考虑如何把这些公用的功能独立出来实现,以便模块化、重用。AOP框架提供一些原语(切面、切入点、通知等),允许将那些业务无关、但是被业务模块共同调用的逻辑或责任封装起来,减少系统的重复代码,降低模块耦合度。更重要的是,业务模块不知道AOP通用模块的存在,这意味着业务模块和通用模块可以独立演化。
装饰模式与AOP在思想上有共同之处,都可以透明的为业务功能对象增加功能,但是AOP的实现方式更加灵活、可配置,且AOP将主动调用变成了被动织入。
            
Leave a Reply