rubyinheritancevs mixin

在Ruby中,由于可以包含多个mixin,但只能扩展一个类,所以mixin似乎比inheritance更受欢迎。

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

我只能想到你想要一个类的一个原因,那就是如果你需要实例化类。 但是,在ActiveRecord :: Base的情况下,你不能直接实例化它。 所以不应该是一个模块呢?

刚刚The Well-Grounded Rubyist (伟大的书,顺便说一下)中读到了这个话题。 作者做得比我更好解释,所以我会引用他的话:


没有一个单一的规则或公式总会导致正确的devise。 但是当你做出类与模块的决定时,记住一些注意事项是有用的:

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

  • 一个类只能有一个超类,但它可以按照需要混合尽可能多的模块。 如果你使用inheritance,优先考虑创build一个合理的超类/子类关系。 不要使用一个类的唯一的超类关系来赋予这个类什么可能只是几个特征之一。

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

module Vehicle ... class SelfPropelling ... class Truck < SelfPropelling include Vehicle ... 

相反,你应该这样做:

 module SelfPropelling ... class Vehicle include SelfPropelling ... class Truck < Vehicle ... 

第二个版本更加整洁地模拟实体和属性。 卡车从车辆下降(这是有道理的),而SelfPropelling是车辆的特征(至less,我们在这个世界模型中关心的所有人) – 卡车是一个传承的特点,卡车是后裔,或特殊forms的车辆。

我认为mixin是一个好主意,但还有另一个没有人提到的问题:命名空间冲突。 考虑:

 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 

哪一个胜利? 在Ruby中,事实certificate是后者, module B ,因为你将它包含在module A 。 现在,很容易避免这个问题:确保module Amodule B的常量和方法都在不太可能的名称空间中。 问题在于,碰撞发生时,编译器根本不会提醒你。

我认为这种行为并没有扩展到大规模的程序员团队 – 你不应该假定实现class C的人知道范围内的每个名字。 Ruby甚至会让你重写一个常量或不同types的方法。 我不确定这可以被认为是正确的行为。

我的意思是:模块用于共享行为,而类则用于build模对象之间的关系。 在技​​术上,你可以把所有东西都做成Object的一个实例,然后混合你想要的任何模块来获得所需的行为,但这将是一个糟糕的,随意的,而且是难以理解的devise。

你的问题的答案很大程度上是背景的。 提炼pubb的观察,select主要由正在考虑的领域驱动。

是的,ActiveRecord应该被包含而不是被子类扩展。 另一个ORM – 数据映射器 – 正是为了达到这个目的!

我非常喜欢Andy Gaskell的答案 – 只是想补充说,是的,ActiveRecord不应该使用inheritance,而是包括一个模块来添加行为(主要是持久性)到模型/类。 ActiveRecord只是使用错误的范例。

出于同样的原因,我非常喜欢MongoMap的MongoMap,因为它使开发人员有机会使用inheritance作为对问题域中有意义的build模方法。

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

现在,我正在考虑templatedevise模式。 这只是一个模块感觉不对。