我正在研究 Node.js 的 EventEmitter 我正在尝试创建一个面包店,当它烤面包时,如果顾客有钱,他们会自动购买面包,但我也想创建一个我的顾客列表可以添加或删除客户,他们只会在添加时列出事件,而在删除时不会再监听,这就是我一直在做的事情:
面包店.js
this._breadPrice = breadPrice
const events = require('./events.js')
const eventEmitter = require('./event-emitter.js');
class Bakery {
_breads;
_costumers;
_breadPrice;
constructor(breadPrice) {
this._breads = 0
this._costumers = []
}
get breadPrice() {
return this._breadPrice
}
bakeBreads(breadsQuantity) {
this._breads += breadsQuantity
eventEmitter.emit(events.BREAD_BAKED, breadsQuantity)
}
addCostumer(costumer) {
// How would i do?
this._costumers.push(costumer)
eventEmitter.on(events.BREAD_BAKED, (breads) => {
costumer.buyBread(breads)
})
costumer.bakery = this
}
removeCostumer(costumer) {
// How would i do?
}
}
module.exports = Bakery
costumer.js
const events = require('./events.js')
const eventEmitter = require('./event-emitter.js')
class Costumer {
_costumerName
_maxPrice
_moneyAmount
_bakery
constructor(costumerName,maxPrice, moneyAmount, bakery) {
this._maxPrice = maxPrice
this._moneyAmount = moneyAmount
this._bakery = bakery
this._costumerName = costumerName
}
set bakery(bakery) {
this._bakery = bakery
}
buyBread() {
if (this._moneyAmount >= this._bakery.breadPrice) {
this._moneyAmount -= this._bakery.breadPrice
console.log(`Costumer ${this._costumerName} bought a bread costing ${this._bakery._breadPrice}`)
return
}
console.log(`${this._costumerName} doesn't have enough money to buy a bread`)
}
}
module.exports = Costumer
main.js
const events = require('./events.js');
const Bakery = require('./bakery.js');
const Costumer = require('./costumer.js');
const eventEmitter = require('./event-emitter.js');
const bakery = new Bakery(2.5)
const johnRich = new Costumer("John", 1, 10);
const martinPoor = new Costumer("Martin", 0.3, 1);
关于如何正确实施有什么想法吗?
在详细讨论事件调度或提供如何从面包店的客户列表中删除客户的解决方案之前,需要改进有关购买和销售的建模方法。
一个可能的解决方案是实施面包店的
sellBread
方法,该方法通过客户参考和要购买的面包块的数量。该方法将控制买方/卖方交易是否成功的整个验证。客户还可以使用“buyBread
”方法,该方法会传递面包店参考信息和已经提到的要购买面包的数量。此方法只会转发到传递的面包店引用的 sellBread
方法,但会返回交易结果,该结果要么成功,要么附带交易失败的原因。
下一个提供的示例代码实现了一个
Bakery
类,该类还扩展了 EventTarget
,以演示如何在不具有自己的客户的 addEVentListener
实例中利用 removeEventListener
/dispatchEvent
和 Bakery
-列表。
// main.js
// const Bakery = require('./bakery.js');
// const Customer = require('./customer.js');
const klugesherz =
new Bakery({ name: 'Pâtisserie Klugesherz', breadPrice: 1.5 });
const hanss =
new Bakery({ name: 'Boulangerie Patisserie Hanss', breadPrice: 2.7 });
const johnRich =
new Customer({ name: 'John Rich', maxPrice: 5, moneyTotal: 30 });
const martinPoor =
new Customer({ name: 'Martin Poor', maxPrice: 3, moneyTotal: 15 });
const handleJohnRichBuysLotsOfBreadEverywhere = ({ currentTarget: bakery }) => {
johnRich.buyBread(bakery, 3);
}
const handleMartinPoorBuysBreadSelectively = ({ currentTarget: bakery }) => {
const quantity = (
((bakery.name === 'Boulangerie Patisserie Hanss') && 1) ||
((bakery.name === 'Pâtisserie Klugesherz') && 2) || 0
);
martinPoor.buyBread(bakery, quantity);
}
klugesherz.addEventListener('bread-baked', handleJohnRichBuysLotsOfBreadEverywhere);
klugesherz.addEventListener('bread-baked', handleMartinPoorBuysBreadSelectively);
hanss.addEventListener('bread-baked', handleJohnRichBuysLotsOfBreadEverywhere);
hanss.addEventListener('bread-baked', handleMartinPoorBuysBreadSelectively);
console.log('\n+++ klugesherz.bakeBread(4) +++');
klugesherz.bakeBread(4);
console.log('\n+++ hanss.bakeBread(5) +++');
hanss.bakeBread(5);
console.log('\n+++ klugesherz.bakeBread(4) +++');
klugesherz.bakeBread(4);
console.log('\n+++ hanss.bakeBread(5) +++');
hanss.bakeBread(5);
console.log('\n+++ klugesherz.bakeBread(4) +++');
klugesherz.bakeBread(4);
console.log('\n+++ hanss.bakeBread(5) +++');
hanss.bakeBread(5);
console.log('\n... remove event listener from Patisserie Hanss which handles John Rich ...\n\n');
hanss.removeEventListener('bread-baked', handleJohnRichBuysLotsOfBreadEverywhere);
console.log('\n+++ klugesherz.bakeBread(4) +++');
klugesherz.bakeBread(4);
console.log('\n+++ hanss.bakeBread(5) +++');
hanss.bakeBread(5);
console.log('\n... remove all other event listeneres ...\n\n');
klugesherz.removeEventListener('bread-baked', handleJohnRichBuysLotsOfBreadEverywhere);
klugesherz.removeEventListener('bread-baked', handleMartinPoorBuysBreadSelectively);
hanss.removeEventListener('bread-baked', handleMartinPoorBuysBreadSelectively);
console.log('+++ klugesherz.bakeBread(4) +++');
klugesherz.bakeBread(4);
console.log('+++ hanss.bakeBread(5) +++');
hanss.bakeBread(5);
.as-console-wrapper { min-height: 100%!important; top: 0; }
<script>
// customer.js
class Customer {
#name;
#maxPrice;
#moneyTotal;
constructor({ name, maxPrice, moneyTotal }) {
this.#name = name;
this.#maxPrice = maxPrice;
this.#moneyTotal = moneyTotal;
}
get name() {
return this.#name;
}
get maxPrice() {
return this.#maxPrice;
}
get moneyTotal() {
return this.#moneyTotal;
}
buyBread(bakery, quantity = 1) {
const { approved, reason } = bakery.sellBread(this, quantity);
if (approved === true) {
this.#moneyTotal = this.moneyTotal - (bakery.breadPrice * quantity);
console.log(
`Customer ${ this.name } bought ${ quantity } piece/s of bread for a total of ${ (bakery.breadPrice * quantity).toFixed(2) } at ${ bakery.name }.`
);
} else if (typeof reason === 'string') {
console.log('Buying a bread did fail, due to ...', reason);
}
}
}
// module.exports = Customer;
</script>
<script>
// bakery.js
class Bakery extends EventTarget {
#name;
#breadPrice;
#breadCount;
#moneyTotal;
constructor({ name, breadPrice }) {
super();
this.#name = name;
this.#breadPrice = breadPrice;
this.#breadCount = 0;
this.#moneyTotal = 0;
}
get name() {
return this.#name;
}
get breadPrice() {
return this.#breadPrice;
}
get breadCount() {
return this.#breadCount;
}
get moneyTotal() {
return this.#moneyTotal;
}
bakeBread(quantity = 10) {
this.#breadCount = this.#breadCount + quantity;
this.dispatchEvent(
new CustomEvent('bread-baked', { detail: { quantity } })
);
}
sellBread(customer, quantity = 1) {
const transaction = { approved: false };
if (quantity >= 1) {
if (this.breadCount >= quantity) {
const isWithinPriceLimit = this.breadPrice <= customer.maxPrice;
const canEffortPurchase = (this.breadPrice * quantity) <= customer.moneyTotal;
if (isWithinPriceLimit) {
if (canEffortPurchase) {
this.#breadCount = this.breadCount - quantity;
transaction.approved = true;
} else {
transaction.reason =
`Customer ${ customer.name } doesn't have enough money for buying a bread at ${ this.name }.`;
}
} else {
transaction.reason =
`Customer ${ customer.name } does have a price limit which just did exceed at ${ this.name }.`;
}
} else {
transaction.reason =
`The ${ this.name } bakery is too low on bread stock in order to fulfill ${ customer.name }'s order.`;
}
} else {
transaction.reason =
`Customer ${ customer.name } did not provide a valid quantity for purchasing bread at ${ this.name }.`;
}
return transaction;
}
}
// module.exports = Bakery;
</script>
上述示例的下一个代码迭代在改进的版本中结合了 OP 的预期
addCustomers
/removeCustomers
和事件处理。
为了解决OP如何正确地将客户从面包店的客户列表中删除的问题,需要再次更改模型。客户必须提供一个函数来处理为任何面包店的自定义
'bread-baked'
活动购买面包。只有这样,面包店的addCustomers
和removeCustomers
才能以有意义的方式实现。
OP 的实际问题可以通过在将项目添加到唯一客户对象映射或从唯一客户对象映射中删除它们之前验证传递给这些方法的项目是否是有效的客户对象来解决。每个新添加的客户都将订阅面包店的
'bread-baked'
活动,而每次删除客户后,客户都会被取消订阅。
// main.js
// const Bakery = require('./bakery.js');
// const { Customer } = require('./customer.js');
const klugesherz =
new Bakery({ name: 'Pâtisserie Klugesherz', breadPrice: 1.5 });
const hanss =
new Bakery({ name: 'Boulangerie Patisserie Hanss', breadPrice: 2.7 });
const johnRich = new Customer({
name: 'John Rich',
maxPrice: 5,
moneyTotal: 30,
handlePurchaseBread: function ({ currentTarget: bakery }) {
this.buyBread(bakery, 3);
},
});
const martinPoor = new Customer({
name: 'Martin Poor',
maxPrice: 3,
moneyTotal: 15,
handlePurchaseBread: function ({ currentTarget: bakery }) {
const quantity = (
((bakery.name === 'Boulangerie Patisserie Hanss') && 1) ||
((bakery.name === 'Pâtisserie Klugesherz') && 2) || 0
);
this.buyBread(bakery, quantity);
},
});
klugesherz.addCustomers(johnRich, martinPoor);
hanss.addCustomers(johnRich, martinPoor);
console.log({
bakeries: {
klugesherz: klugesherz.valueOf(),
hanss: hanss.valueOf(),
},
customers: {
johnRich: johnRich.valueOf(),
martinPoor: martinPoor.valueOf(),
},
});
console.log('\n+++ klugesherz.bakeBread(4) +++');
klugesherz.bakeBread(4);
console.log('\n+++ hanss.bakeBread(5) +++');
hanss.bakeBread(5);
console.log('\n+++ klugesherz.bakeBread(4) +++');
klugesherz.bakeBread(4);
console.log('\n+++ hanss.bakeBread(5) +++');
hanss.bakeBread(5);
console.log('\n+++ klugesherz.bakeBread(4) +++');
klugesherz.bakeBread(4);
console.log('\n+++ hanss.bakeBread(5) +++');
hanss.bakeBread(5);
console.log('\n... remove John Rich from the customer list of Patisserie Hanss.\n\n');
hanss.removeCustomers(johnRich);
console.log('\n+++ klugesherz.bakeBread(4) +++');
klugesherz.bakeBread(4);
console.log('\n+++ hanss.bakeBread(5) +++');
hanss.bakeBread(5);
console.log('\n... remove Martin Poor from the customer list of Patisserie Hanss.');
hanss.removeCustomers(martinPoor);
console.log('... remove John Rich and Martin Poor from the customer list of Pâtisserie Klugesherz.\n\n');
klugesherz.removeCustomers(johnRich, martinPoor);
console.log('+++ klugesherz.bakeBread(4) +++');
klugesherz.bakeBread(4);
console.log('+++ hanss.bakeBread(5) +++');
hanss.bakeBread(5);
.as-console-wrapper { min-height: 100%!important; top: 0; }
<script>
// customer.js
class Customer {
#name;
#maxPrice;
#moneyTotal;
#handlePurchaseBread;
constructor({ name, maxPrice, moneyTotal, handlePurchaseBread }) {
this.#name = name;
this.#maxPrice = maxPrice;
this.#moneyTotal = moneyTotal;
this.#handlePurchaseBread = (typeof handlePurchaseBread === 'function')
&& handlePurchaseBread.bind(this)
|| (({ currentTarget: bakery }) => { this.buyBread(bakery, 1); });
}
get name() {
return this.#name;
}
get maxPrice() {
return this.#maxPrice;
}
get moneyTotal() {
return this.#moneyTotal;
}
get handlePurchaseBread() {
return this.#handlePurchaseBread;
}
buyBread(bakery, quantity = 1) {
const { approved, reason } = bakery.sellBread(this, quantity);
if (approved === true) {
this.#moneyTotal = this.moneyTotal - (bakery.breadPrice * quantity);
console.log(
`Customer ${ this.name } bought ${ quantity } piece/s of bread for a total of ${ (bakery.breadPrice * quantity).toFixed(2) } at ${ bakery.name }.`
);
} else if (typeof reason === 'string') {
console.log('Buying a bread did fail, due to ...', reason);
}
}
valueOf() {
const { name, maxPrice, moneyTotal, handlePurchaseBread } = this;
return { name, maxPrice, moneyTotal, handlePurchaseBread };
}
}
function isCustomer(value) {
return ((value instanceof Customer) || (
Object
.keys(value.valueOf())
.sort()
.join('_') === 'handlePurchaseBread_maxPrice_moneyTotal_name'
));
}
// module.exports = { Customer, isCustomer };
</script>
<script>
// bakery.js
// const { isCustomer } = require('./customer.js');
class Bakery extends EventTarget {
#name;
#breadPrice;
#breadCount;
#moneyTotal;
#customers;
constructor({ name, breadPrice }) {
super();
this.#name = name;
this.#breadPrice = breadPrice;
this.#breadCount = 0;
this.#moneyTotal = 0;
this.#customers = new Map;
}
get name() {
return this.#name;
}
get breadPrice() {
return this.#breadPrice;
}
get breadCount() {
return this.#breadCount;
}
get moneyTotal() {
return this.#moneyTotal;
}
get customers() {
return [...this.#customers.values()];
}
addCustomers(...customers) {
customers
.flat()
.filter(isCustomer)
.forEach(customer => {
const { name } = customer;
if (!this.#customers.has(name)) {
this.#customers.set(name, customer);
this.addEventListener('bread-baked', customer.handlePurchaseBread);
}
});
}
removeCustomers(...customers) {
customers
.flat()
.filter(isCustomer)
.forEach(customer => {
const { name } = customer;
if (this.#customers.has(name)) {
this.#customers.delete(name);
this.removeEventListener('bread-baked', customer.handlePurchaseBread);
}
});
}
getCustomersByName(...names) {
names
.flat()
.filter(item => (typeof item === 'string'))
.reduce((result, name) => {
if (this.#customers.has(name)) {
result[name] = this.#customers.get(name);
}
return result;
}, {});
}
bakeBread(quantity = 10) {
this.#breadCount = this.#breadCount + quantity;
this.dispatchEvent(
new CustomEvent('bread-baked', { detail: { quantity } })
);
}
sellBread(customer, quantity = 1) {
const transaction = { approved: false };
if (quantity >= 1) {
if (this.breadCount >= quantity) {
const isWithinPriceLimit = this.breadPrice <= customer.maxPrice;
const canEffortPurchase = (this.breadPrice * quantity) <= customer.moneyTotal;
if (isWithinPriceLimit) {
if (canEffortPurchase) {
this.#breadCount = this.breadCount - quantity;
transaction.approved = true;
} else {
transaction.reason =
`Customer ${ customer.name } doesn't have enough money for buying a bread at ${ this.name }.`;
}
} else {
transaction.reason =
`Customer ${ customer.name } does have a price limit which just did exceed at ${ this.name }.`;
}
} else {
transaction.reason =
`The ${ this.name } bakery is too low on bread stock in order to fulfill ${ customer.name }'s order.`;
}
} else {
transaction.reason =
`Customer ${ customer.name } did not provide a valid quantity for purchasing bread at ${ this.name }.`;
}
return transaction;
}
valueOf() {
const { name, breadPrice, breadCount, moneyTotal, customers } = this;
return {
name, breadPrice, breadCount, moneyTotal,
customers: customers.map(customer => customer.valueOf()),
};
}
}
// module.exports = Bakery;
</script>