装饰者模式
装饰者模式指的是在不改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。装饰者可以动态地将责任附加到对象上。
装饰者模式组成结构
- 抽象构件 (Component):给出抽象接口或抽象类,以规范准备接收附加功能的对象。
- 具体构件 (ConcreteComponent):定义将要接收附加功能的类。
- 抽象装饰 (Decorator):装饰者共同要实现的接口,也可以是抽象类。
- 具体装饰 (ConcreteDecorator):持有一个 Component 对象,负责给构件对象“贴上”附加的功能。
UML类图

使用场景
- 需要扩展一个类的功能,或给一个类添加附加职责。
- 需要动态的给一个对象添加功能,这些功能可以再动态的撤销。
- 需要增加由一些基本功能的排列组合而产生的非常大量的功能,从而使继承关系变的不现实。
星巴兹咖啡订单系统
星巴兹店提供了各式各样的咖啡,以及各种咖啡调料。为了适应饮料需求供应,所以让你设计一个更新订单系统
继承

在购买咖啡时,可以要求在咖啡中添加各种调料,例如:豆浆 (Soy)、摩卡 (Mocha)、奶泡等。由于各种调料的价格不相同,所以订单系统必须要考虑这些因素。
继承类图

类爆炸
装饰者登场
装饰者模式涉及到的一个重要的设计原则 (当然还涉及到了其他的设计原则,比如多用组合,少用继承等):类应该对扩展开放,对修改关闭。
在设计过程中,我们允许类容易扩展,在不修改原有代码的情况下,就可以扩展新的行为。这样的设计具有弹性可以应对改变,可以接受新的功能来应对新的需求。
想要摩卡和豆浆深焙咖啡,那么,要做的是:
- 拿一个深焙咖啡 (
DarkRoast) 对象 - 以摩卡 (
Mocha) 装饰它 - 以豆浆 (
Soy) 装饰它 - 调用
cost()方法,并依赖委托将调料的价钱加上去

装饰者模式设计图

饮料 Beverage 抽象类
public abstract class Beverage { protected String description = "Unknown Beverage"; public String getDescription() { return description; } /** * 价格 * * @return */ public abstract double getCost(); }
浓咖啡 Espresso 类
public class Espresso extends Beverage { @Override public String getDescription() { return "Espresso"; } @Override public double getCost() { return 1.99; } }
黑咖啡HouseBlend类
public class HouseBlend extends Beverage { @Override public String getDescription() { return "House Blend Coffee"; } @Override public double getCost() { return 0.8; } }
调料类CondimentDecorator类
public abstract class CondimentDecorator extends Beverage { public abstract String getDescription(); }
摩卡Mocha类
public class Mocha extends CondimentDecorator { private Beverage beverage; public Mocha(Beverage beverage) { this.beverage = beverage; } @Override public String getDescription() { return beverage.getDescription() + ",Mocha"; } @Override public double getCost() { return 0.2 + beverage.getCost(); } }
豆浆Soy类
public class Soy extends CondimentDecorator { private Beverage beverage; public Soy(Beverage beverage) { this.beverage = beverage; } @Override public String getDescription() { return beverage.getDescription() + ",Soy"; } @Override public double getCost() { return 0.1 + beverage.getCost(); } }
测试类
@Slf4j public class BuyCoffee { public static void main(String[] args) { //两份摩卡加一份豆浆的浓咖啡 Beverage espresso = new Espresso(); espresso = new Mocha(espresso); espresso = new Mocha(espresso); espresso = new Soy(espresso); log.info("{} 价格:{}", espresso.getDescription(), espresso.getCost()); } }
装饰者模式问题总结
- 装饰者与被装饰对象有相同的超类型 (
DarkRoast与装饰类Mocha和Soy都继承自Beverage(饮料))。 - 可以使用一个或多个装饰对象包装一个对象。
- 因为装饰者与被装饰者具有相同的超类型,在任何需要原始对象的情况下,都可以用装饰过的对象去代替它。
- 装饰者可以在所委托被装饰者的行为之前与之后,加上自己的行为,以达到特定的目的。
- 对象可以在任何时候被装饰,所以在运行时动态地、不限量地用你喜欢的装饰者去装饰对象
立志如山 静心求实
浙公网安备 33010602011771号