在 java.io 包中,不仅 OutputStream 用到了 Decorator 设计模式,InputStream,Reader,Writer 等都用到了此模式。而作为一个灵活的,可扩展的类库,JDK 中使用了大量的设计模式,比如在 Calendar 的单例模式,线程池 的工厂模式等等。对于 JDK 源码中设计模式的研究不仅能加深对于设计模式的理解,而且还有利于更透彻的了解类库的结构和组成。
模式动机
模式名称:装饰模式 (Decorator)
有时我们希望给某个对象而不是整个类添加一些功能。例如:一个图形用户界面工具箱允许我们对任意一个用户界面组件添加一些新的特性,如增加一个边框;或者增加一些行为,如窗口的滚动。 一种较为灵活的方式是将组件嵌入另一个对象中,由这个对象来添加边框。我们称这个嵌入的对象为装饰器(Decorator)。
这个装饰与它所装饰的组件接口 一致,因此它对使用该组件的客户透明。它将客户请求转发给该组件,并且可能在转发前后执行一些额外的动作。这种透明性使得我们可以递归嵌套多个装饰,从而可以添加任意多的功能。
模式定义
装饰模式(Decorator Pattern):动态地给一个对象增加一些额外的职责 (Responsibility),就增加对象功能来说,装饰模式比生成子类实现更为灵活。其别名为包装器(Wrapper)。装饰模式是一种对象结构型模式。
模式结构
- Component:抽象组件
- ConcreteComponent:具体组件
- Decorator:抽象装饰类
- ConcreteDecoratorA:具体装饰类
- ConcreteDecoratorB:具体装饰类
装饰模式的优缺点
- 装饰模式与继承关系的目的都是要扩展对象的功能,但是装饰模式可以提供比继承更多的灵活性。
- 通过使用不同的具体装饰类以及这些装饰类的排列组合,设计师可以创造出很多不同行为的组合。
- 这种比继承更加灵活机动的特性,也同时意味着装饰模式比继承更加易于出错。
模式使用场景
在以下情况下适合使用装饰模式:
- 在不影响其它对象的情况下,以动态、透明的方式给单个对象添加职责。需要动态地给一个对象增加功能,这些功能可以再动态地被撤销。
- 当不能采用生成子类的方法进行扩充时。一种情况是,可能有大量独立的扩展,每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。另一种情况可能是因为类定义不能继承(final类),或类不能用于生成子类。
注意事项
- 一个装饰类的接口必须与被装饰类的接口保持相同,对于客户端来说无论是装饰之前的对象还是装饰之后的对象都可以一致对待。
- 尽量保持具体构件类Component作为一个“轻”类,也就是说不要把太多的逻辑和状态放在具体构件类中,可以通过装饰类对其进行扩展。