Reputation: 15488
It appears to be common practice to inherit from the EventEmitter if you want your class to support events. For example Google does it for Puppeteer, the WebSocket module does it, mongoose does it, ... just to name a few.
But is this really good practice? I mean sure it looks nice and clean, but from an OOP perspective it seems wrong. For example:
const EventEmitter = require('events')
class Rectangle extends EventEmitter {
constructor(x,y,w,h) {
super()
this.position = {x:x, y:y}
this.dimensions = {w:w, h:h}
}
setDimensions(w,h) {
this.dimensions = {w:w, h:h}
this.emit('dimensionsChanged')
}
}
would make it seem like Rectangle is an EventEmitter at its core even though the eventing functionality is secondary.
What if you decide that Rectangle
now needs to inherit from a new class called Shape
?
class Shape {
constructor(x,y) {
this.position = {x:x, y:y}
}
}
class Rectangle extends Shape {
constructor(x,y,w,h) {
super(x,y)
this.dimensions = {w:w, h:h}
}
}
Now you would have to make Shape
inherit from the EventEmitter. Even if only one class inheriting from Shape
actually needs eventing.
Wouldn't something like this make way more sense?
class Shape {
constructor(x,y) {
this.position = {x, y}
}
}
const EventEmitter = require('events')
class Rectangle extends Shape {
constructor(x,y,w,h) {
super(x,y)
this.dimensions = {w, h}
this.em = new EventEmitter()
}
setDimensions(w,h) {
this.dimensions = {w:w, h:h}
this.em.emit('dimensionsChanged')
}
}
const rectangle = new Rectangle(1,2,3,4)
rectangle.em.on('dimensionsChanged', ()=>{console.log('dimensions changed')})
rectangle.setDimensions(10,20)
Upvotes: 6
Views: 851
Reputation: 18473
Yes, it absolutely makes more sense.
Inheritance should be used to denote is a relationships: class Rectangle extends Shape
is ok since Rectangle
is a shape, but here the rectangle itself is not an EventEmitter
.
Instead, we have an can relationship: Rectangle
can emit an event, and that is exactly where you should favor composition over inheritance, which is what happens in your last code snippet.
We can only speculate as to why some famous libs don't do that - retro-compatibility, simplicity of APIs, or simply bad design.
Upvotes: 4