ruby继承vs mixins

Bra*_*pit 125 ruby inheritance module class mixins

在Ruby中,既然你可以包含多个mixin但只扩展一个类,那么看起来mixins比继承更受欢迎.

我的问题:如果你正在编写必须扩展/包含的代码才有用,你为什么要把它变成一个类?换句话说,为什么你不总是把它变成一个模块?

我只能想到你想要一个类的一个原因,那就是你需要实例化这个类.但是,在ActiveRecord :: Base的情况下,您永远不会直接实例化它.所以不应该是一个模块而不是?

And*_*ell 176

刚刚The Well-Grounded Rubyist中阅读了这个主题(顺便一下,这本书很棒).作者比我更好地解释,所以我引用他的话:


没有单一的规则或公式总能产生正确的设计.但是,当您进行类与模块决策时,记住一些注意事项是有用的:

  • 模块没有实例.因此,实体或事物通常最好地在类中建模,并且实体或事物的特征或属性最好地封装在模块中.相应地,如4.1.1节所述,类名往往是名词,而模块名通常是形容词(Stack与Stacklike).

  • 一个类只能有一个超类,但它可以根据需要混合使用多个模块.如果您正在使用继承,请优先创建合理的超类/子类关系.不要使用类的唯一超级关系来赋予类可能只是几组特征中的一种.

在一个例子中总结这些规则,这是你不应该做的:

module Vehicle 
... 
class SelfPropelling 
... 
class Truck < SelfPropelling 
  include Vehicle 
... 
Run Code Online (Sandbox Code Playgroud)

相反,你应该这样做:

module SelfPropelling 
... 
class Vehicle 
  include SelfPropelling 
... 
class Truck < Vehicle 
... 
Run Code Online (Sandbox Code Playgroud)

第二个版本更加整洁地模拟实体和属性.卡车来自车辆(这是有意义的),而自动推进是车辆的特征(至少,我们在这个世界模型中所关心的所有人) - 一个特征,由于卡车是后代传递给卡车,车辆的特殊形式.


Dan*_*owy 39

我认为mixins是一个好主意,但是这里还有另一个问题,没有人提到过:命名空间冲突.考虑:

module A
  HELLO = "hi"
  def sayhi
    puts HELLO
  end
end

module B
  HELLO = "you stink"
  def sayhi
    puts HELLO
  end
end

class C
  include A
  include B
end

c = C.new
c.sayhi
Run Code Online (Sandbox Code Playgroud)

哪一个获胜?在Ruby中,事实证明是后者module B,因为你之后将它包括在内module A.现在,很容易避免这个问题:确保所有module Amodule B的常量和方法都在不太可能的命名空间中.问题是编译器在发生冲突时根本不会发出警告.

我认为这种行为无法扩展到大型程序员团队 - 你不应该假设实施者class C知道范围内的每个名字.Ruby甚至可以让你覆盖不同类型的常量或方法.我不知道,可能曾经被认为是正确的行为.

  • 在大多数环境中,此行为是不可接受的 (4认同)
  • 这是明智之举.让人联想到C++中的多重继承陷阱. (2认同)

Chu*_*uck 12

我的看法:模块用于共享行为,而类用于建模对象之间的关系.从技术上讲,你可以将所有东西都作为Object的一个实例,并混合在你希望得到所需行为的任​​何模块中,但这将是一个糟糕的,偶然的,相当难以理解的设计.

  • 这以一种直接的方式回答了这个问题:继承强制执行一种特定的组织结构,该结构可以使您的项目更具可读性。 (2认同)

nar*_*shb 10

你的问题的答案主要是语境.提炼pubb的观察,选择主要由所考虑的领域驱动.

是的,ActiveRecord应该被包含在内而不是由子类扩展.另一个ORM - 数据映射器 - 正是实现了这一目标!


Til*_*ilo 5

我非常喜欢 Andy Gaskell 的回答 - 只是想补充一点,是的,ActiveRecord 不应该使用继承,而应该包含一个模块来将行为(主要是持久性)添加到模型/类中。ActiveRecord 只是使用了错误的范例。

出于同样的原因,我非常喜欢 MongoId 而不是 MongoMapper,因为它让开发人员有机会使用继承来对问题领域中有意义的内容进行建模。

令人遗憾的是,Rails 社区中几乎没有人按照应有的方式使用“Ruby 继承”——定义类层次结构,而不仅仅是添加行为。