【发布时间】:2017-04-06 01:28:25
【问题描述】:
我更多是出于好奇而不是真正关心它来问这个问题,但我一直想知道 JavaScript 事件系统是否违反了Liskov substitution principle (LSP)。 p>
通过调用EventTarget.dispatchEvent,我们可以调度一个任意类型的Event,它可能由注册的EventListener处理。
interface EventListener {
void handleEvent(in Event evt);
}
如果我正确理解 LSP,则意味着 anyEventListener.handleEvent(anyEvent) 不应该失败。但是,通常情况并非如此,因为事件侦听器通常会使用专门的 Event 子类型的属性。
在不支持泛型的类型化语言中,该设计基本上需要将Event 对象向下转换为EventListener 中的预期子类型。
据我了解,上述设计可被视为违反 LSP。我是正确的还是通过EventTarget.addEventListener 注册侦听器时必须提供type 的简单事实可以防止LSP 违规?
编辑:
虽然每个人似乎都在关注 Event 子类没有违反 LSP 的事实,但我实际上担心 EventListener 实现者会通过加强 @987654338 的前置条件来违反 LSP @的界面。 void handleEvent(in Event evt) 合约中的任何内容都没有告诉您传递错误的 Event 子类型可能会破坏某些内容。
在具有泛型的强类型语言中,接口可以表示为EventListener<T extends Event>,以便实现者可以使合同明确,例如SomeHandler implements EventListener<SomeEvent>.
在 JS 中显然没有实际的接口,但事件处理程序仍然需要符合规范,并且该规范中没有任何内容允许处理程序判断它是否可以处理特定类型的事件。
这不是一个真正的问题,因为侦听器不应该被自己调用,而是由注册它的EventTarget 调用并与特定类型关联。。 p>
我只是对根据理论是否违反 LSP 感兴趣。我想知道是否要避免违反(如果从理论上考虑的话)合同需要类似于以下内容(尽管它在实用主义方面可能做得比好事多):
interface EventListener {
bool handleEvent(in Event evt); //returns wheter or not the event could be handled
}
【问题讨论】:
-
LSP 是关于预期对象行为的。除了通常理想的“不应该失败”之外,由程序员决定给定应用程序中的预期行为是什么。现在事件系统只是一个框架,用于将处理程序绑定到事件名称并确保它们在正确的时刻被调用。这些处理程序只是用户定义的函数。没有什么可以阻止他们以不同的方式处理基类和子类,这允许违反您决定对系统强加的任何常见行为规则,只要您愿意,就可以经常和鲁莽地违反。所以我的猜测是,答案是否定的。
-
我不确定我是否有资格回答这个问题,所以我会发表评论。只要 Event 子类型包含/具有与 Event 相同的所有相同属性(相同类型)和相同签名和返回类型的相同方法,那么它不满足 LSP 吗?此调用
anyEventListener.handleEvent(anyEvent)由于缺少 Event 子类型的属性而失败是处理程序设计的错误,不是吗?如果它只使用 Event 类型的属性,那么无论使用哪种 sup-type 调用它都不会失败。这不就是 LSP 定义的吗? -
@kuroineko 这是一个很好的答案。不知道为什么它只是一个评论。
-
@JoshfromQaribou 好吧,这种学术性的头发分裂并不是我真正喜欢的。我敢肯定,很多人靠着夸夸其谈 SOLID 及其许多良好实践而过着丰盛的生活。我说让他们玩得开心...
-
@kuroineko 这绝对是一个理论问题而不是实际问题;)
标签: javascript dom-events liskov-substitution-principle