从 EventEmitter 继承是一种反模式吗?

问题描述

如果您希望您的类支持事件,那么从 EventEmitter 继承似乎是一种常见的做法。 例如,Google 为 Puppeteer 执行此操作,WebSocket module 执行此操作,mongoose does it,...仅举几例。

但这真的是好习惯吗?我的意思是肯定它看起来漂亮干净,但从 OOP 的角度来看,它似乎是错误的。例如:

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')
    }
}

即使事件功能是次要的,也会使 Rectangle 看起来像是一个 EventEmitter 的核心。

如果您决定 Rectangle 现在需要从名为 Shape 的新类继承怎么办?

class Shape {
    constructor(x,y) {
        this.position = {x:x,y:y}
    }
}

class Rectangle extends Shape {
    constructor(x,h) {
        super(x,y)
        this.dimensions = {w:w,h:h}
    }
}

现在您必须使 Shape 从 EventEmitter 继承。即使只有一个继承自 Shape 的类实际上需要事件处理。

这样的事情不会更有意义吗?

class Shape {
    constructor(x,y) {
        this.position = {x,y}
    }
}

const EventEmitter = require('events')

class Rectangle extends Shape {
    constructor(x,y)
        this.dimensions = {w,h}
        this.em = new EventEmitter()
    }
    setDimensions(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)

解决方法

是的,这绝对更有意义。

应该使用继承来表示 is a 关系:class Rectangle extends Shape 是可以的,因为 Rectangle 是一个形状,但这里的矩形本身不是一个 em> EventEmitter

相反,我们有一个 can 关系:Rectangle 可以发出一个事件,这正是您应该支持 composition over inheritance 的地方,它是在您的最后一个代码片段中发生的事情。

我们只能推测为什么一些著名的库不这样做 - 复古兼容性、API 的简单性,或者只是糟糕的设计。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...