在Sandi Metz的OO Design in Ruby一书中,模块和继承有什么区别?



在第153页,她区分了is-abehaves-like的差异。继承对应于is-a而模块对应于behaves-like。但真正的区别是什么?这两种技术都依赖于通过自动消息委派的消息,对吗?

在她在"理解角色"一章中使用的示例中,似乎主要区别在于模块更适合鸭子类型,并且用于使看似陌生的实体适合某些角色?角色到底是什么?任何有发言权Schedulable都需要响应模块的接口,因此可以在任何预期只是响应该接口的role的地方进行替换。

而在继承章节中,梅斯似乎只是定义了一个层次结构,而没有同样关注鸭子类型。通过她的自行车示例,她解决了共享共同行为但具有某些专用化子类的相关类型的问题。

但是,存在共性。两者都仍然将抽象行为提升到共享类或模块中。两者都仍然具有子类,这些子类实现可用于鸭子类型目的的相同接口。那么真正的区别是什么呢?

模块部分比继承部分更强调鸭子类型。这是为什么呢?

有几个区别,但在某些方面它们的行为相同。主要区别在于模块可以临时包含,混合和匹配,而您只能从单个类"继承"。但最终,混合和继承的实现方式相同:模块或类被添加到祖先链中,并用于在响应消息时查找方法。在继承的情况下,您定义的类将添加到祖先链的末尾。模块可以包含在链中的不同位置(通常在您要扩展的类之前(,但是一旦它们在那里,它们的行为就相同。

另一个区别是继承更经常修改或定义初始化。(尽管可以使用模块执行此操作,但您很少看到这种情况(。如果您关心对象的初始状态,则可能需要使用继承。

至于鸭子类型,模块通常被认为是类似于具有内置实现的java接口。在java中,由于所有内容都是强类型的,因此您必须明确说明对象是否"嘎嘎"。接口就是这样做的,并且是"类似行为"的。模块是最接近 ruby 中的东西。

一般来说,模块由于其灵活性而往往更易于使用,但模块更加抽象,并且必须比基类具有更大的关注点分离。

我是这样看的。 我是一个人,那么作为一个人,我是一个男人。 我的妻子也是人,但她是女人,但我们都扮演着父母的角色。 父母不是男人或女人,而是扮演特定角色的人。因此,定义该行为的代码可以放在模块中并由两个类共享。

大多数现代语言都有扩展的概念,其中可以将常见行为添加到现有类中。 还有更多的例子,但 Kotlin 和 Swift 都在他们的标准库中使用它,他们通过重用在一个地方定义的代码来向许多类添加常见行为。

Java在为接口添加默认实现时也添加了这个概念。

最新更新