I'm studying about the EventEmitter of Node.js. I'm trying to create a Bakery where customers automatically buy bread if they have money when it bakes bread. Still, I also want to create a list of customers that I can add or remove customers, and they only will listen to the event when they're added and will not listen anymore when removed. This is how I've been doing it:
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;
}
bakeBreads(breadsQuantity) {
this._breads += breadsQuantity;
eventEmitter.emit(events.BREAD_BAKED, breadsQuantity);
}
addCustomer(customer) {
// How would i do?
this._customers.push(customer);
eventEmitter.on(events.BREAD_BAKED, (breads) => {
customer.buyBread(breads);
})
customer.bakery = this;
}
removeCustomer(customer) {
// How would i do?
}
}
module.exports = Bakery;
customer.js
class Customer {
_customerName;
_maxPrice;
_moneyAmount;
_bakery;
constructor(customerName, maxPrice, moneyAmount, bakery) {
this._maxPrice = maxPrice;
this._moneyAmount = moneyAmount;
this._bakery = bakery;
this._customerName = customerName;
}
set bakery(bakery) {
this._bakery = bakery;
}
buyBread() {
if (this._moneyAmount >= this._bakery.breadPrice) {
this._moneyAmount -= this._bakery.breadPrice;
console.log(`Customer ${this._customerName} bought a bread costing ${this._bakery._breadPrice}`);
return;
}
console.log(`${this._customerName} doesn't have enough money to buy a bread`);
}
}
module.exports = Customer;
main.js
const Bakery = require('./bakery.js');
const Customer = require('./customer.js');
const bakery = new Bakery(2.5)
const johnRich = new Customer('John', 1, 10);
const martinPoor = new Customer('Martin', 0.3, 1);
Do you have any ideas on how to implement it correctly?
A possible solution to the OP's problem comes with two main design changes which again cause some more changes within the models and implementations as well.
Customer objects are stored within a
Mapinstance under a customer's name where a customer's name is supposed to be unique.A customer either gets provided its own function of how to handle the purchase of bread from a bakery which did just notify about newly baked bread or this handler function does get assigned as default implementation to each customer object at its instantiation time.
Thus, the changes to the
Customerclass are less invasive than the ones to theBakeryclass.customer.js
The changes a bakery needs to undergo are as follows ...
addCustomer/removeCustomeraccording toWith implementing point 2) it comes to the light that a customer should be capable of buying bread from whichever bakery.
It is the direct result of the OP's design of adding/removing customers to a bakery's customer list and dispatching "bread-baked" events to every registered customer. Thus there is no value in assigning a single bakery instance to a customer object. On the other hand it should be clear that the dispatched data has to provide not only the amount of freshly baked pieces of bread but also the bakery which was in charge of baking it all ...
Which directly causes the change of the arguments-signature of a customer's
buyBreadmethod. Its first argument has to be a bakery reference and its second argument has to be the amount of to be purchased pieces of bread with a default of 1.Furthermore, there are changes which target the privacy and protection of fields. The OP uses the underscore for the annotation of pseudo-private fields. On the other hand the OP uses get methods for accessing such already public fields like with a bakery's
_breadPriceversusbreadPrice. In this case, and some others too, it is totally fine to make use of true private propertiesIn addition and most importantly, the OP seems to use a single
EventEmitterinstance for every to be createdBakeryinstance. But a correctly targeted event/data-dispatching relies on the loose relationship in between a bakery instance and all of its registered customers. Thus, every bakery object needs its ownEventEmitterinstance.bakery.js
Edit
Within a next code iteration the modeling approach regarding buying and selling really needs to be improved.
A possible solution was to implement a bakery's
sellBreadmethod which gets passed a customer-reference and the amount of pieces of to be purchased bread. This method would be in control of the entire validation of whether a buyer/seller transaction is/was successful. A customer likewise would features abuyBreadmethod which gets passed a bakery-reference and the already mentioned amount of pieces of to be purchased bread. This method would just forward to the passed bakery-reference'ssellBreadmethod, but would return a transaction result which either is successful or comes with a reason of why a transaction did fail.The next provided example code implements a
Bakeryclass which in addition extendsEventTargetin order to demonstrate how one does utilizeaddEVentListener/removeEventListeneranddispatchEventdirectly atBakeryinstances.On top there are other code improvements like verifying whether the items passed to
addCustomers/removeCustomersare valid customer-objects.