Skip to main content

装饰器模式 (Decorator)

场景问题​

问题案例​

考虑这样一个实际的业务场景:咖啡订单系统。用户可以点一杯基础咖啡,也可以选择加牛奶、加糖、加巧克力等配料。每种配料都可以叠加,最终需要计算总价并输出描述。

不用模式的解决方案​

实现简化版本​

最直接的做法是为每种组合写一个类,例如:

public class Coffee {
public String getDescription() { return "咖啡"; }
public double cost() { return 10.0; }
}

public class CoffeeWithMilk extends Coffee {
@Override
public String getDescription() { return super.getDescription() + "+牛奶"; }
@Override
public double cost() { return super.cost() + 2.0; }
}

public class CoffeeWithMilkAndSugar extends CoffeeWithMilk {
@Override
public String getDescription() { return super.getDescription() + "+糖"; }
@Override
public double cost() { return super.cost() + 1.0; }
}

使用示例​

public class Client {
public static void main(String[] args) {
Coffee coffee = new CoffeeWithMilkAndSugar();
System.out.println(coffee.getDescription() + " 总价:" + coffee.cost());
}
}

有何问题​

这种实现方式存在明显问题:

  1. 每增加一种配料组合都要写一个新类,类爆炸。
  2. 组合不灵活,不能动态叠加。
  3. 代码重复,维护困难。

解决方案​

装饰器模式的定义​

动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式比生成子类更为灵活。

应用装饰器模式的思路​

  • 定义一个统一的组件接口(如Beverage),基础对象和装饰器都实现该接口。
  • 装饰器持有一个组件对象的引用,并在扩展功能时调用原有对象的方法。
  • 可以动态、灵活地叠加多个装饰器。

装饰器模式的结构​

  • Beverage:抽象组件,定义统一接口。
  • Coffee:具体组件,实现基础功能。
  • Decorator:抽象装饰器,持有组件引用,扩展功能。
  • Milk/Sugar:具体装饰器,实现具体的附加功能。

装饰器模式的实现​

// 统一接口
public interface Beverage {
String getDescription();
double cost();
}

// 具体组件
public class Coffee implements Beverage {
public String getDescription() { return "咖啡"; }
public double cost() { return 10.0; }
}

// 抽象装饰器
public abstract class Decorator implements Beverage {
protected Beverage beverage;
public Decorator(Beverage beverage) { this.beverage = beverage; }
}

// 具体装饰器:牛奶
public class Milk extends Decorator {
public Milk(Beverage beverage) { super(beverage); }
public String getDescription() { return beverage.getDescription() + "+牛奶"; }
public double cost() { return beverage.cost() + 2.0; }
}

// 具体装饰器:糖
public class Sugar extends Decorator {
public Sugar(Beverage beverage) { super(beverage); }
public String getDescription() { return beverage.getDescription() + "+糖"; }
public double cost() { return beverage.cost() + 1.0; }
}

使用示例​

public class Client {
public static void main(String[] args) {
Beverage coffee = new Coffee();
coffee = new Milk(coffee); // 加牛奶
coffee = new Sugar(coffee); // 加糖
System.out.println(coffee.getDescription() + " 总价:" + coffee.cost());
}
}

装饰器重写示例​

示例结构​

用mermaid画出装饰器叠加的结构关系:


示例代码​

见上文"装饰器模式的实现"部分。


模式讲解​

装饰器模式的功能​

  • 动态、灵活地为对象添加新功能。
  • 可以任意组合、叠加装饰器。
  • 不影响原有类结构,符合开闭原则。

实现成接口​

  • 抽象组件通常定义为接口或抽象类。
  • 装饰器和具体组件都实现统一接口。

使用工厂方法​

  • 可结合工厂方法模式,统一创建装饰器链。

切换产品簇​

  • 装饰器模式可用于切换不同的功能组合,如不同饮品配料。

执行时序图​