Decorator 装饰器模式。

装饰器模式主要解决继承关系过于复杂的问题,通过组合来替代继承。它主要的作用是给原始类添加增强功能。这也是判断是否该用装饰器模式的一个重要的依据。除此之外,装饰器模式还有一个特点,那就是可以对原始类嵌套使用多个装饰器。

为了满足这个应用场景,在设计的时候,装饰器类需要跟原始类继承相同的抽象类或者接口。

定义

动态(组合)地给一个对象增加一些额外的职责。就增加功能而言,Decorator 模式比生成子类(继承)更为灵活(消除重复代码和减少子类个数)== 《设计模式》GoF

UML

DP_decorator_uml

几条原则

来自《Head First 设计模式》的几条原则。

  • 装饰者和被装饰者对象有相同的超类型。
  • 你可以用一个或多个装饰者包装一个对象。
  • 既然装饰者和被装饰者对象有相同的超类型,所以在任何需要原始对象(被包装的)的场合,可以用装饰过的对象代替它。
  • 装饰者可以在所委托被装饰者的行为之前或之后,加上自己的行为,以达到特定的目的。
  • 对象可以在任何时候被装饰,所以可以在运行时动态地、不限量地用你喜欢的装饰者来装饰对象。

示例

interface Coffee {
  public double getCost();

  public String getIngredients();
}

class SimpleCoffee implements Coffee {
  @Override
  public double getCost() {
    return 1;
  }

  public String getIngredients() {
    return "Coffee";
  }
}
abstract class CoffeeDecorator implements Coffee {
  private final Coffee decoratedCoffee;

  public CoffeeDecorator(Coffee c) {
    this.decoratedCoffee = c;
  }

  @Override
  public double getCost() {
    return decoratedCoffee.getCost();
  }

  @Override
  public String getIngredients() {
    return decoratedCoffee.getIngredients();
  }
}

class WithMilk extends CoffeeDecorator {
  public WithMilk(Coffee c) {
    super(c);
  }

  @Override
  public double getCost() {
    return super.getCost() + 0.5;
  }

  @Override
  public String getIngredients() {
    return super.getIngredients() + ", Milk";
  }
}

class WithSprinkles extends CoffeeDecorator {
  public WithSprinkles(Coffee c) {
    super(c);
  }

  @Override
  public double getCost() {
    return super.getCost() + 0.2;
  }

  @Override
  public String getIngredients() {
    return super.getIngredients() + ", Sprinkles";
  }
}
public class Main {
  public static void printInfo(Coffee c) {
    System.out.println("Cost: " + c.getCost() + "; Ingredients: " + c.getIngredients());
  }

  public static void main(String[] args) {
    Coffee c = new SimpleCoffee();
    printInfo(c);

    c = new WithMilk(c);
    printInfo(c);

    c = new WithSprinkles(c);
    printInfo(c);
  }
}

// Cost: 1.0; Ingredients: Coffee
// Cost: 1.5; Ingredients: Coffee, Milk
// Cost: 1.7; Ingredients: Coffee, Milk, Sprinkles

要点总结

通过采用组合而非继承的手法,Decorator 模式实现了在运行时动态扩展对象功能的能力,而且可以根据需要扩展多个功能。避免了使用继承带来的“灵活性差”和“多子类衍生问题”。

Decorator 类在接口上表现为 is-a Component 的继承关系,即 Decorator 类继承了 Component 类所具有的接口。但在实现上又表现为 has-a Component 的组合关系,即 Decorator 类又使用了另一个 Component 类。

Decorator 模式的目的并非解决“多子类衍生的多继承”问题,Decorator 模式应用的要点在于解决“主体类在多个方向上的扩展功能”–是为“装饰”的含义。

个人观点

装饰器模式是最能体现开放封闭原则的一种模式。

装饰器模式是组合优先于继承最直观的一种模式。

参考链接