使用基类和基本模块重构ActiveRecord模型

时间:2009-11-10 16:29:55

标签: ruby-on-rails ruby inheritance activerecord mixins

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

比其他方式更优惠吗?

5 个答案:

答案 0 :(得分: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”。如果它们具有相同的属性,则必须在两个表中重复列,但如果存在差异,则不会对它们进行反复化。

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

答案 1 :(得分:5)

这两种方法都有效。在决定使用模块或类时,我遇到的问题是类是否适合对象层次结构,或者这些只是我希望重用的方法。如果我只是试图将干扰原因的公共代码分解出来,那听起来像是一个模块。如果真的有一个类适合于自己有意义的层次结构,我会使用一个类。

来自Java背景,我可以选择做出这些决定。

答案 2 :(得分:4)

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

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

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

答案 3 :(得分:1)

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

另一个区别是,在Base类的方法中你可以从ActiveRecord :: Base调用东西,但你不能从模块中做到这一点。

答案 4 :(得分:1)

这取决于你真正想做的事情。

  1. 覆盖或添加方法到ActiveRecord :: Base :如果您希望应用中的每个ActiveRecord模型都respond_to foo,请执行此操作。
  2. Subclass ActiveRecord :: Base,并且每个模型都继承自您的子类:实现与1相同,但您的应用中的每个模型都需要扩展一个非常规类,所以为什么要经历麻烦
  3. 包含模块:如果只有少数模型需要访问foo,这项功能非常有用。这几乎就是所有acts_as_<whatever>个插件所做的。
  4. 最重要的是,如果您希望每个模型都具有与ActiveRecord :: Base已提供的不同的行为,请使用选项1.如果只有少数模型需要该行为,请创建模块并将其包含在模型中(备选方案3)。