装饰模式
装饰模式动态的将责任附加到对象上。想要扩展功能,装饰者提供有别于继承的另一种选择。
类图

Component 是抽象类,所有的被装饰对象都继承它
ConcreteComponent 是被装饰对象
Decorator 继承Component,所有装饰类都继承它
ConcreteDecoratorA,ConcreteDecoratorB是装饰类
要点
- 继承属于扩展形式之一,但不见得是达到弹性设计的最佳方案
- 在我们的设计中,应该允许行为可以被扩展,而无需修改现有的代码
- 组合和委托可用于在运行时动态的加上新的行为
- 除了继承,装饰模式也可以让我们扩展行为
- 装饰者模式意味着一群装饰者类,这些类用来包装具体组件
- 装饰者类反映出被装饰的组件类型(事实上,他们具有相同的类型,都经过接口或继承实现)
- 装饰者可以在被装饰者的行为前面 与/或 后面加上自己的行为,甚至将被装饰者的行为整个取代掉,而达到特定的目的
- 你可以用无数个装饰者包装一个组件
- 装饰者一般对组件的客户是透明的,除非客户程序依赖于组件的具体类型
- 装饰者会导致设计中出现许多小对象,如果过度使用,会让程序变得复杂
例子
一个咖啡店的例子。这是一个订单系统,Beverage是饮料的抽象类,所有饮料都继承它。cost()是计算价格,子类必须实现它
购买咖啡时,客户可以要求在其中加入各种调料,例如:牛奶,豆浆,巧克力,糖等。然后将不同调料和饮料组合之后
显然不能这么设计
如果我们之间把各种调料当做 boolean变量,放在Beverage超类中呢
存在的问题:
- 调料价格的改变会使我们修改现有的代码
- 一旦出现新的调料,我们就需要加上新的变量,并改变超类中的cost()方法
- 以后可能会有新饮料,,对于这些饮料而言,某些调料可能并不适合
- 万一顾客想要双倍糖,怎么办。
设计原则:类应该对扩展开发,对修改关闭
下面我们使用装饰模式解决这个问题
Beverage还是饮料的抽象类,所有的饮料都继承它。增加抽象类 CondimentDecorator,所有的调料都继承它。每种调料都有一个实例变量Beverage,扩展了它的cost()方法,
1 | public abstract class Beverage{ |
饮料1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17public class Espresso extends Beverage{
public Espresso(){
description = "Espresso";
}
public double cost(){
return 1.99;
}
}
public class HouseBlend extends Beverage{
public HouseBlend(){
description = "HouseBlend";
}
public double cost(){
return 0.98;
}
}
调料1
2
3
4
5
6
7
8
9
10
11
12
13
14
15public class Mocha extends CondimentDecorator{
Beverage beverage;
public Mocha(Beverage beverage){
this.beverage = beverage;
}
public String getDescription(){
return beverage.getDescription()+",Mocha";
}
public double cost(){
return 0.20+beverage.cost();
}
}
测试1
2
3
4
5
6
7
8
9
10
11public class StarbuzzCoffee{
public static void main(String[] args){
Beverage beverage = new Espresso();
//加双倍抹茶
beverage = new Mocha(beverage);
beverage = new Mocha(beverage);
System.out.println(beverage.getDescription());
System.out.println("价格="+beverage.cost());
}
}