Forivin
Forivin

Reputation: 15488

Is inheriting from the EventEmitter an antipattern?

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

Answers (1)

Nino Filiu
Nino Filiu

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

Related Questions