Decorator Design Pattern - tenji/ks GitHub Wiki

装饰者/装饰器模式

装饰器是一种结构设计模式(structural design pattern),可让你将新行为附加到对象,方法是将这些对象放置在包含该行为的特殊包装对象(special wrapper objects)内。

Wrapper Pattern 与 Decorator Pattern 的区别?

“Wrapper” is the alternative nickname for the Decorator pattern that clearly expresses the main idea of the pattern. A wrapper is an object that can be linked with some target object.

实质上两者指的是一种设计模式。

一、结构

1.1 类图(静态结构)

decorator-pattern-class-structure

1.2 时序图(动态结构)

decorator-pattern-seq-structure

二、代码示例

decorator-pattern-demo-class-structure

Coffee 类(Component Interface)

// Coffee.java
public interface Coffee {
    String getDescription();
    double getCost();
}

PlainCoffee 类(ConcreteComponent)

// PlainCoffee.java
public class PlainCoffee implements Coffee {
    @Override
    public String getDescription() {
        return "Plain Coffee";
    }

    @Override
    public double getCost() {
        return 2.0;
    }
}

CoffeeDecorator 类(Base Decorator)

// CoffeeDecorator.java
public abstract class CoffeeDecorator implements Coffee {
    protected Coffee decoratedCoffee;

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

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

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

MilkDecorator 类(ConcreteDecorator1)

// MilkDecorator.java
public class MilkDecorator extends CoffeeDecorator {
    public MilkDecorator(Coffee decoratedCoffee) {
        super(decoratedCoffee);
    }

    @Override
    public String getDescription() {
        return decoratedCoffee.getDescription() + ", Milk";
    }

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

SugarDecorator 类(ConcreteDecorator2)

// SugarDecorator.java
public class SugarDecorator extends CoffeeDecorator {
    public SugarDecorator(Coffee decoratedCoffee) {
        super(decoratedCoffee);
    }

    @Override
    public String getDescription() {
        return decoratedCoffee.getDescription() + ", Sugar";
    }

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

Driver Program

// Main.java
public class Main {
    public static void main(String[] args) {
        // Plain Coffee
        Coffee coffee = new PlainCoffee();
        System.out.println("Description: " + coffee.getDescription());
        System.out.println("Cost: $" + coffee.getCost());

        // Coffee with Milk
        Coffee milkCoffee = new MilkDecorator(new PlainCoffee());
        System.out.println("\nDescription: " + milkCoffee.getDescription());
        System.out.println("Cost: $" + milkCoffee.getCost());

        // Coffee with Sugar and Milk
        Coffee sugarMilkCoffee = new SugarDecorator(new MilkDecorator(new PlainCoffee()));
        System.out.println("\nDescription: " + sugarMilkCoffee.getDescription());
        System.out.println("Cost: $" + sugarMilkCoffee.getCost());
    }
}

三、优点

  • 扩展性:你可以扩展对象的行为,而无需创建新的子类;
  • 运行时:你可以在运行时添加或删除对象的职责;
  • 组合:你可以通过将对象包装到多个装饰器中来组合多种行为;
  • 满足单一责任原则。你可以将实现许多可能的行为变体的整体类划分为几个较小的类。

四、缺点

  • 复杂度:当你向对象添加更多装饰器时,代码可能会变得更加复杂且难以理解。装饰器的嵌套可能会使代码库难以导航和调试,尤其是当涉及许多装饰器时;
  • 类数量:使用 Decorator Pattern 时,你通常会得到大量小型的、专门的装饰器类。这可能会导致代码库中类的激增,从而可能增加维护开销;
  • 装饰顺序问题:应用装饰器的顺序会影响对象的最终行为。如果未按正确的顺序应用装饰器,可能会导致意外结果。管理装饰器的顺序可能具有挑战性,尤其是在复杂的场景中。
  • 某些语言的有限支持:某些编程语言可能不为实现装饰器提供方便的支持。在此类语言中,实现该模式可能会更加冗长且不太直观。

五、什么时候适合使用 Decorator Pattern?

  1. 当你需要能够在运行时为对象分配额外的行为而不破坏使用这些对象的代码时,请使用 Decorator Pattern;
  2. 当使用继承来扩展对象的行为很困难或不可能时,请使用该模式。

六、Decorator Pattern vs Proxy Pattern

关于两者的对比,Proxy Design Pattern

∞、参考链接