从概念上讲,是在实现一个抽象内容的同时,以增强或扩展其功能为目的,对它进行一层层,可互相嵌套式的,能灵活添加或去除且顺序可变的包装。
从UML类图结构上来说,它是类的多态特性的扩展。除了‘具体构建对象’对‘抽象构建接口’的独立实现外,在‘装饰器(接口实现类)’构建时还允许将‘抽象构建接口’作为构建方法的入参,被‘装饰器’当作成员属性持有,最终由‘实际的装饰器对象’继承‘装饰器’来实现具体的功能,并允许‘实际的装饰器对象’可以相互嵌套已实现功能的叠加和扩展。
图示:装饰器模式的UML类图结构
图例说明:
Component:抽象构建接口。描述需要抽象的内容
ConcreteComponent:独立的构建对象。通过Java泛化来实现抽象构建接口。
Decorator:装饰器抽象父类。所有的子装饰器对象的统一父类,需要内部持有一个Component并在构建方法中入参这个Component,并同时实现Component接口。
ConreteDecoratorA、ConreteDecoratorB:实际的装饰器对象。具体负责扩展Component的功能。
Java中如何使用装饰器模式
日常生活中的饮品种类多不胜数,但是它们的配料内容大部分是相同的,比如绿茶就是开水冲茶叶,奶茶就是已经制作好的绿茶中添加鲜牛奶,当然奶茶也可以先煮鲜牛奶,再放入茶叶。像生活中这类制作顺序可灵活变化的事物,就可以用装饰器模式来描述。
图示:用修饰器模式来表示饮品
装饰器模式中都有一个同源的接口类(抽象构建接口类),描述需要构想的内容。在这里用IDrink这个接口来描述‘饮品’这一抽象类。
装饰器模式中还需要一个统一的装饰器抽象父类,这里用DrinkDecorator抽象类来代表:
例子中WaterDrink是直接用继承的方式实现IDrink,这里作为最地基础的原料(饮品当然水是最基本的嘛)。
之后的TeaDrink(茶饮)、MilkyDrink(奶饮)、FruitDrink(果饮)都是用WaterDrink来制作的(从UML上来说,就是内部持有IDrink的实现类)
就像例子开头举得奶茶的例子,就可以用UML图做如下表达:
图示:奶茶的UML表达
代码实现如下:
执行结果:
成分配料:饮用水;高品质茶叶;鲜牛奶;
制作方法:->添加饮用水->开水煮茶叶->混合新鲜牛奶
同理,如果想更复杂一些,比如水果味奶茶,可以用UML图描述如下:
图示:果味奶茶UML表达
代码如下,结果就不重复了
Java中的装饰器模式的优缺点
灵活的组合是最大的优点。装饰器模式与类的泛化都是对类功能的扩展,但使用装饰器模式,从代码维护上来讲,可以更简单的为对象“添加一个装饰(包裹一层功能外衣)”,“去除一个装饰(脱下一层)”,顺序也可以方便的调整,可以通过不同的组合实现各色各样的业务场景。
缺点也很明显,它会产生很多的装饰对象。如果不加以设计和充分描述,其他程序员会较难理解并使用各个装饰对象。