咖啡机进阶优化

使用设计模式优化咖啡机代码,实现装饰器和建造者模式

问题

基于基础咖啡机实现,使用设计模式进行优化改造,提升代码的扩展性和可维护性。

解答

基础版本(存在的问题)

// 基础版本 - 扩展性差,添加新配料需要修改类
class Coffee {
  constructor(type) {
    this.type = type;
    this.price = 10;
    this.ingredients = [];
  }

  addMilk() {
    this.ingredients.push('milk');
    this.price += 3;
    return this;
  }

  addSugar() {
    this.ingredients.push('sugar');
    this.price += 1;
    return this;
  }

  // 每添加一种配料就要新增方法...
}

优化一:装饰器模式

// 咖啡基类
class Beverage {
  getDescription() {
    return 'Unknown Beverage';
  }

  cost() {
    return 0;
  }
}

// 具体咖啡
class Espresso extends Beverage {
  getDescription() {
    return 'Espresso';
  }

  cost() {
    return 15;
  }
}

class Latte extends Beverage {
  getDescription() {
    return 'Latte';
  }

  cost() {
    return 20;
  }
}

// 装饰器基类
class CondimentDecorator extends Beverage {
  constructor(beverage) {
    super();
    this.beverage = beverage;
  }
}

// 具体装饰器 - 牛奶
class Milk extends CondimentDecorator {
  getDescription() {
    return `${this.beverage.getDescription()}, Milk`;
  }

  cost() {
    return this.beverage.cost() + 3;
  }
}

// 具体装饰器 - 摩卡
class Mocha extends CondimentDecorator {
  getDescription() {
    return `${this.beverage.getDescription()}, Mocha`;
  }

  cost() {
    return this.beverage.cost() + 5;
  }
}

// 具体装饰器 - 奶泡
class Whip extends CondimentDecorator {
  getDescription() {
    return `${this.beverage.getDescription()}, Whip`;
  }

  cost() {
    return this.beverage.cost() + 2;
  }
}

// 使用示例
let coffee = new Espresso();
coffee = new Milk(coffee);
coffee = new Mocha(coffee);
coffee = new Whip(coffee);

console.log(coffee.getDescription()); // Espresso, Milk, Mocha, Whip
console.log(coffee.cost()); // 25

优化二:建造者模式

// 咖啡产品
class Coffee {
  constructor() {
    this.type = '';
    this.size = 'medium';
    this.milk = false;
    this.sugar = 0;
    this.extras = [];
  }

  describe() {
    const parts = [this.size, this.type];
    if (this.milk) parts.push('with milk');
    if (this.sugar > 0) parts.push(`${this.sugar} sugar`);
    if (this.extras.length) parts.push(this.extras.join(', '));
    return parts.join(' ');
  }
}

// 建造者
class CoffeeBuilder {
  constructor() {
    this.coffee = new Coffee();
  }

  setType(type) {
    this.coffee.type = type;
    return this;
  }

  setSize(size) {
    this.coffee.size = size;
    return this;
  }

  addMilk() {
    this.coffee.milk = true;
    return this;
  }

  addSugar(amount = 1) {
    this.coffee.sugar = amount;
    return this;
  }

  addExtra(extra) {
    this.coffee.extras.push(extra);
    return this;
  }

  build() {
    return this.coffee;
  }
}

// 指挥者 - 预设配方
class Barista {
  static makeAmericano(builder) {
    return builder
      .setType('Americano')
      .setSize('large')
      .build();
  }

  static makeCapuccino(builder) {
    return builder
      .setType('Capuccino')
      .addMilk()
      .addExtra('foam')
      .build();
  }

  static makeMocha(builder) {
    return builder
      .setType('Mocha')
      .addMilk()
      .addExtra('chocolate')
      .addExtra('whip cream')
      .build();
  }
}

// 使用示例
// 自定义咖啡
const customCoffee = new CoffeeBuilder()
  .setType('Latte')
  .setSize('small')
  .addMilk()
  .addSugar(2)
  .addExtra('vanilla')
  .build();

console.log(customCoffee.describe());
// small Latte with milk 2 sugar vanilla

// 使用预设配方
const mocha = Barista.makeMocha(new CoffeeBuilder());
console.log(mocha.describe());
// medium Mocha with milk chocolate, whip cream

优化三:结合 TypeScript 和事件系统

// 类型定义
type CoffeeSize = 'small' | 'medium' | 'large';
type CoffeeEvent = 'brewing' | 'ready' | 'error';

interface CoffeeOptions {
  type: string;
  size: CoffeeSize;
  milk: boolean;
  sugar: number;
}

// 事件发射器
class EventEmitter {
  private events: Map<string, Function[]> = new Map();

  on(event: string, callback: Function) {
    if (!this.events.has(event)) {
      this.events.set(event, []);
    }
    this.events.get(event)!.push(callback);
    return this;
  }

  emit(event: string, ...args: any[]) {
    const callbacks = this.events.get(event);
    if (callbacks) {
      callbacks.forEach(cb => cb(...args));
    }
  }
}

// 咖啡机
class CoffeeMachine extends EventEmitter {
  private queue: CoffeeOptions[] = [];
  private isBrewing = false;

  // 添加订单
  order(options: CoffeeOptions) {
    this.queue.push(options);
    this.processQueue();
    return this;
  }

  // 处理队列
  private async processQueue() {
    if (this.isBrewing || this.queue.length === 0) return;

    this.isBrewing = true;
    const order = this.queue.shift()!;

    this.emit('brewing', order);

    // 模拟制作过程
    await this.brew(order);

    this.emit('ready', order);
    this.isBrewing = false;

    // 继续处理下一个
    this.processQueue();
  }

  private brew(options: CoffeeOptions): Promise<void> {
    const time = options.size === 'large' ? 3000 : 2000;
    return new Promise(resolve => setTimeout(resolve, time));
  }
}

// 使用示例
const machine = new CoffeeMachine();

machine
  .on('brewing', (order: CoffeeOptions) => {
    console.log(`正在制作: ${order.size} ${order.type}`);
  })
  .on('ready', (order: CoffeeOptions) => {
    console.log(`完成: ${order.type}`);
  });

machine
  .order({ type: 'Latte', size: 'medium', milk: true, sugar: 1 })
  .order({ type: 'Espresso', size: 'small', milk: false, sugar: 0 });

关键点

  • 装饰器模式:动态添加功能,避免类爆炸,配料可自由组合
  • 建造者模式:分步构建复杂对象,链式调用提升可读性
  • 指挥者角色:封装常用配方,简化客户端代码
  • 事件驱动:解耦制作过程和状态通知,支持异步操作
  • TypeScript:类型约束减少运行时错误,提升代码可维护性