JavaScript设计模式「基于ES2024」:结构型模式-装饰器模式

150 阅读2分钟

装饰器模式允许开发者通过将对象放入包含行为的特殊封装对象中来为原对象动态地添加新的行为。这种模式在不修改现有代码的情况下,灵活地扩展对象的功能。

// 基础组件接口
class Coffee {
    getDescription() {
        throw new Error('Method not implemented');
    }

    cost() {
        throw new Error('Method not implemented');
    }
}

// 具体组件
class SimpleCoffee extends Coffee {
    getDescription() {
        return "Simple Coffee";
    }

    cost() {
        return 2;
    }
}

// 装饰器基类
class CoffeeDecorator extends Coffee {
    #wrappedCoffee;

    constructor(coffee) {
        super();
        this.#wrappedCoffee = coffee;
    }

    getDescription() {
        return this.#wrappedCoffee.getDescription();
    }

    cost() {
        return this.#wrappedCoffee.cost();
    }
}

// 具体装饰器
class MilkDecorator extends CoffeeDecorator {
    constructor(coffee) {
        super(coffee);
    }

    getDescription() {
        return `${super.getDescription()}, Milk`;
    }

    cost() {
        return super.cost() + 0.5;
    }
}

class SugarDecorator extends CoffeeDecorator {
    constructor(coffee) {
        super(coffee);
    }

    getDescription() {
        return `${super.getDescription()}, Sugar`;
    }

    cost() {
        return super.cost() + 0.2;
    }
}

class WhippedCreamDecorator extends CoffeeDecorator {
    constructor(coffee) {
        super(coffee);
    }

    getDescription() {
        return `${super.getDescription()}, Whipped Cream`;
    }

    cost() {
        return super.cost() + 0.7;
    }
}

// 咖啡订单类
class CoffeeOrder {
    #coffee;

    constructor() {
        this.#coffee = new SimpleCoffee();
    }

    addMilk() {
        this.#coffee = new MilkDecorator(this.#coffee);
        return this;
    }

    addSugar() {
        this.#coffee = new SugarDecorator(this.#coffee);
        return this;
    }

    addWhippedCream() {
        this.#coffee = new WhippedCreamDecorator(this.#coffee);
        return this;
    }

    get description() {
        return this.#coffee.getDescription();
    }

    get cost() {
        return this.#coffee.cost();
    }
}

// 使用示例
function orderCoffee() {
    console.log("Ordering a simple coffee:");
    let order = new CoffeeOrder();
    console.log(`Description: ${order.description}`);
    console.log(`Cost: $${order.cost.toFixed(2)}`);

    console.log("\nOrdering a complex coffee:");
    order = new CoffeeOrder()
        .addMilk()
        .addSugar()
        .addWhippedCream();
    console.log(`Description: ${order.description}`);
    console.log(`Cost: $${order.cost.toFixed(2)}`);
}

orderCoffee();

实现思路

  1. Coffee:这是基础组件接口,定义了所有咖啡对象必须实现的方法。

  2. SimpleCoffee:这是具体组件,实现了基本的咖啡。

  3. CoffeeDecorator:这是装饰器基类,它持有一个被包装的咖啡对象,并实现了 Coffee 接口。

    • 使用私有字段 #wrappedCoffee 来存储被装饰的咖啡对象。
  4. 具体装饰器类MilkDecorator, SugarDecorator, WhippedCreamDecorator):

    • 这些类继承自 CoffeeDecorator,并在基础咖啡的基础上添加新的行为(增加描述和成本)。
  5. CoffeeOrder:这是一个方便的类,用于构建咖啡订单。

    • 使用私有字段 #coffee 来存储当前的咖啡对象。
    • 提供了添加不同配料的方法,每个方法都返回 this,允许方法链式调用。
    • 使用 getter 来获取最终的描述和成本。

优点

  • 开闭原则:可以在不修改现有代码的情况下添加新的装饰器。
  • 单一职责原则:每个装饰器只负责一个特定的功能。
  • 组合优于继承:通过组合实现了更灵活的功能扩展。
  • 运行时动态组合:可以在运行时动态地添加或移除功能。