You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Node.js EventEmitter实现顾客订阅取消事件及顾客列表管理

面包店EventEmitter场景完整实现方案

你的代码目前存在几个核心问题:事件回调无法被取消引用、顾客移除逻辑缺失、购买逻辑未用到预设的最高价和烤好的面包数量。以下是针对性的修正方案:

核心修改说明

  1. 可追踪的事件回调:匿名回调无法通过off方法取消订阅,因此需要将回调函数绑定到顾客实例上,方便后续移除。
  2. 完整的顾客移除逻辑:既要从面包店的顾客列表中删除,也要同步取消对应的事件订阅。
  3. 完善的购买逻辑:加入顾客对面包价格的接受上限(maxPrice)判断,同时结合面包库存处理购买行为。

修改后的bakery.js

const eventEmitter = require('./event-emitter.js');
const events = require('./events.js');

class Bakery {
  _breads;
  _customers;
  _breadPrice;

  constructor(breadPrice) {
    this._breads = 0;
    this._customers = [];
    this._breadPrice = breadPrice;
  }

  get breadPrice() {
    return this._breadPrice;
  }

  get breads() {
    return this._breads;
  }

  bakeBreads(breadsQuantity) {
    this._breads += breadsQuantity;
    console.log(`面包店烤好了 ${breadsQuantity} 个面包,当前库存:${this._breads}`);
    eventEmitter.emit(events.BREAD_BAKED, breadsQuantity);
  }

  addCustomer(customer) {
    // 避免重复添加同一顾客
    if (this._customers.includes(customer)) return;
    
    this._customers.push(customer);
    // 将回调函数绑定到顾客实例,用于后续取消订阅
    customer.buyCallback = () => {
      customer.buyBread();
    };
    // 订阅烤面包完成事件
    eventEmitter.on(events.BREAD_BAKED, customer.buyCallback);
    
    customer.bakery = this;
    console.log(`顾客 ${customer._customerName} 已加入面包店`);
  }

  removeCustomer(customer) {
    // 从顾客列表中移除目标顾客
    this._customers = this._customers.filter(c => c !== customer);
    // 取消对应的事件订阅
    if (customer.buyCallback) {
      eventEmitter.off(events.BREAD_BAKED, customer.buyCallback);
      delete customer.buyCallback;
    }
    customer.bakery = null;
    console.log(`顾客 ${customer._customerName} 已离开面包店`);
  }
}
module.exports = Bakery;

修改后的customer.js

class Customer {
  _customerName;
  _maxPrice;
  _moneyAmount;
  _bakery;

  constructor(customerName, maxPrice, moneyAmount, bakery = null) {
    this._customerName = customerName;
    this._maxPrice = maxPrice;
    this._moneyAmount = moneyAmount;
    this._bakery = bakery;
  }

  set bakery(bakery) {
    this._bakery = bakery;
  }

  buyBread() {
    if (!this._bakery) return;
    
    // 三重判断:价格符合预期、有足够资金、面包店有库存
    if (this._bakery.breadPrice > this._maxPrice) {
      console.log(`${this._customerName} 觉得面包太贵,放弃购买`);
      return;
    }
    if (this._moneyAmount < this._bakery.breadPrice) {
      console.log(`${this._customerName} 资金不足,买不起面包`);
      return;
    }
    if (this._bakery.breads <= 0) {
      console.log(`${this._customerName} 想买面包,但店里已售罄`);
      return;
    }

    // 执行购买操作
    this._moneyAmount -= this._bakery.breadPrice;
    this._bakery._breads -= 1; // 购买一个面包,库存减1
    console.log(`✅ ${this._customerName} 花费 ${this._bakery.breadPrice} 元购买面包,剩余余额:${this._moneyAmount.toFixed(2)},面包店剩余库存:${this._bakery.breads}`);
  }
}
module.exports = Customer;

修改后的main.js(测试用例)

const Bakery = require('./bakery.js');
const Customer = require('./customer.js');

// 假设你的event-emitter.js是Node原生events的实例封装:
// module.exports = new(require('events').EventEmitter)();
// events.js定义事件常量:module.exports = { BREAD_BAKED: 'bread_baked' };

const bakery = new Bakery(2.5);
const johnRich = new Customer('John', 3, 10); // John能接受最高3元,持有10元
const martinPoor = new Customer('Martin', 3, 1); // Martin能接受最高3元,仅持有1元

// 添加顾客
bakery.addCustomer(johnRich);
bakery.addCustomer(martinPoor);

// 第一次烤面包
console.log('\n--- 第一次烤面包 ---');
bakery.bakeBreads(2);

// Martin离开面包店
console.log('\n--- Martin离开面包店 ---');
bakery.removeCustomer(martinPoor);

// 第二次烤面包
console.log('\n--- 第二次烤面包 ---');
bakery.bakeBreads(3);

关键细节说明

  • 事件订阅与取消:通过将回调函数挂载到customer.buyCallback,移除时可直接用该引用调用eventEmitter.off,解决了匿名函数无法取消订阅的问题。
  • 购买逻辑优化:补充了价格预期、库存判断,让场景更贴近真实业务逻辑。
  • 重复添加防护:addCustomer中加入重复判断,避免同一顾客被多次订阅事件。

内容的提问来源于stack exchange,提问作者dvgga

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 05:23:21