盒子
盒子
文章目录
  1. 装饰模式
    1. 类图
  2. 要点
  3. 例子

设计模式之装饰模式

装饰模式

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

类图

装饰类图
Component 是抽象类,所有的被装饰对象都继承它
ConcreteComponent 是被装饰对象
Decorator 继承Component,所有装饰类都继承它
ConcreteDecoratorA,ConcreteDecoratorB是装饰类


要点

  • 继承属于扩展形式之一,但不见得是达到弹性设计的最佳方案
  • 在我们的设计中,应该允许行为可以被扩展,而无需修改现有的代码
  • 组合和委托可用于在运行时动态的加上新的行为
  • 除了继承,装饰模式也可以让我们扩展行为
  • 装饰者模式意味着一群装饰者类,这些类用来包装具体组件
  • 装饰者类反映出被装饰的组件类型(事实上,他们具有相同的类型,都经过接口或继承实现)
  • 装饰者可以在被装饰者的行为前面 与/或 后面加上自己的行为,甚至将被装饰者的行为整个取代掉,而达到特定的目的
  • 你可以用无数个装饰者包装一个组件
  • 装饰者一般对组件的客户是透明的,除非客户程序依赖于组件的具体类型
  • 装饰者会导致设计中出现许多小对象,如果过度使用,会让程序变得复杂

例子

一个咖啡店的例子。这是一个订单系统,Beverage是饮料的抽象类,所有饮料都继承它。cost()是计算价格,子类必须实现它
装饰1

购买咖啡时,客户可以要求在其中加入各种调料,例如:牛奶,豆浆,巧克力,糖等。然后将不同调料和饮料组合之后
装饰2
显然不能这么设计

如果我们之间把各种调料当做 boolean变量,放在Beverage超类中呢
装饰3

存在的问题:

  • 调料价格的改变会使我们修改现有的代码
  • 一旦出现新的调料,我们就需要加上新的变量,并改变超类中的cost()方法
  • 以后可能会有新饮料,,对于这些饮料而言,某些调料可能并不适合
  • 万一顾客想要双倍糖,怎么办。

设计原则:类应该对扩展开发,对修改关闭

下面我们使用装饰模式解决这个问题
Beverage还是饮料的抽象类,所有的饮料都继承它。增加抽象类 CondimentDecorator,所有的调料都继承它。每种调料都有一个实例变量Beverage,扩展了它的cost()方法,
装饰4

1
2
3
4
5
6
7
8
9
10
11
public abstract class Beverage{
String description = "Unknown Beverage";
public String getDescription(){
return description;
}
public abstract double cost();
}

public abstract class CondimentDecorator extends Beverage{
public abstract String getDescription();
}

饮料

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public 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
15
public 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
11
public 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());
}
}