13

A 类和 B 类是相同的:

class A < ActiveRecord::Base
 def foo
  puts "foo"
 end
end

class B < ActiveRecord::Base
 def foo
  puts "foo"
 end
end

像这样用基类重构有什么区别:

class Base < ActiveRecord::Base
 def foo
  puts "foo"
 end
end

class A < Base
end

class B < Base
end

与使用基本模块的情况相比:

module Base
 def foo
  puts "foo"
 end
end

class A < ActiveRecord::Base
 include Base
end

class B < ActiveRecord::Base
 include Base
end

一种方式优于另一种方式吗?

4

5 回答 5

24

这两种方法之间存在根本区别,所有其他答案都缺失了,这就是 rails 对 STI(单表继承)的实现:

http://api.rubyonrails.org/classes/ActiveRecord/Base.html(找到“单表继承”部分)

基本上,如果你像这样重构你的 Base 类:

class Base < ActiveRecord::Base
  def foo
    puts "foo"
  end
end

class A < Base
end

class B < Base
end

然后,您应该有一个名为“bases”的数据库表,其中有一列名为“type”,其值应为“A”或“B”。此表上的列在您的所有模型中都是相同的,如果您的列仅属于其中一个模型,则您的“基础”表将被非规范化。

然而,如果你像这样重构你的 Base 类:

Module Base
  def foo
  puts "foo"
 end
end

class A < ActiveRecord::Base
 include Base
end

class B < ActiveRecord::Base
 include Base
end

那么就不会有表“基地”。相反,将有一个表“as”和一个表“bs”。如果它们具有相同的属性,则必须在两个表中复制这些列,但如果存在差异,则不会对它们进行 denomarlized。

所以,如果一个比另一个更可取,是的,但那是特定于您的应用程序的。根据经验,如果它们具有完全相同的属性或有很大的重叠,请使用 STI(第一个示例),否则,使用模块(第二个示例)。

于 2009-11-11T00:23:14.853 回答
5

这两种方法都可以。在决定使用模块或类时,我的问题是该类是否适合对象层次结构,或者这些只是我希望重用的方法。如果我只是出于 DRY 的原因尝试排除通用代码,那听起来像是一个模块。如果真的有一个适合自己有意义的层次结构的类,我会使用一个类。

来自 Java 背景,令人耳目一新的是,我可以选择做出这些决定。

于 2009-11-10T16:40:39.070 回答
4

您对模块有更大的灵活性。该模块的目的是跨越不同类型的类。使用另一种方法,您将自己锁定在 Base 中。除此之外,没有太大区别。

Ruby 对多重继承的回答是 mixins。由于您的类已经从 Rails 特定类继承,它们不能再从您的自定义类继承。

所以你的选择是在一个长链中链接在一起,或者使用一个更干净、更容易理解的 mixin。

于 2009-11-10T16:41:55.583 回答
1

该模块为您提供了更大的灵活性:1)您只能从一个类继承,但您可以包含多个模块,以及 2)您不能从基类继承而不继承其超类,但您可以全部包含一个模块本身(例如,您可能希望将“foo”方法添加到另一个不是活动记录模型的类)。

另一个区别是,在 Base 类的方法中,您可以从 ActiveRecord::Base 调用事物,但不能从模块中调用。

于 2009-11-10T16:40:43.103 回答
1

这取决于你真正想要做什么。

  1. 覆盖或向 ActiveRecord::Base 添加方法:如果您希望应用程序中的每个 ActiveRecord 模型都使用respond_to foo.
  2. 子类 ActiveRecord::Base,并且让每个模型都继承自您的子类:实现与 1 相同,但是您的应用程序中的每个模型都需要扩展一个非常规的类,所以为什么要这么麻烦。
  3. 包含模块:如果只有一些模型需要访问,这很有效foo。这几乎就是所有这些acts_as_<whatever>插件所做的。

底线,如果您希望每个模型具有与 ActiveRecord::Base 已经提供的不同的行为,请使用选项 1。如果只有少数模型需要该行为,请创建一个模块并将其包含在您的模型中(选项 3 )。

于 2009-11-10T16:41:28.850 回答