【问题标题】:Is it meaningful to override the '==' method in ActiveRecord subclasses?覆盖 ActiveRecord 子类中的 '==' 方法有意义吗?
【发布时间】:2009-06-24 13:55:13
【问题描述】:

Rails 的 ActiveRecord::Base 类定义了一个 == 方法,如果对象相同或具有相同的 ID,则返回 true

我已经在我的几个 Rails 模型中覆盖了==,以允许更有意义的相等条件。当我直接比较对象时(例如,通过script/console),这些工作,但如果我做类似my_array_of_models.include? other_modelinclude? 总是返回false。即使数组包含一个“相等”的对象(根据我的定义)。

我已经解决了这个问题,例如,my_array_of_models.any? { |el| el.attr == other_model.attr }(我认为这是鼓励您进行比较的方式,无论如何),但我想知道:在 ActiveRecord 中覆盖 == 是否有意义模型,还是 ActiveRecord 做了一些高级别的事情,使这种被覆盖的方法无用(或更糟,危险)?

来源

这是我的重写方法的实现。有两个类,UserContactUsers 具有唯一的电子邮件地址,因此如果电子邮件地址相同,== 将返回 true。 ContactUsers 之间的桥梁(就像社交网络中的“朋友”关系),如果它们具有相同的user_id,则应返回true

class User < ActiveRecord::Base
  def ==(other)
    other.respond_to?(:email) and self.email == other.email
  end
end

class Contact < ActiveRecord::Base
  def ==(other)
    if other.class == self.class
      self.user == other.user
    elsif other.kind_of? User
      self.user == other
    else
      false
    end
  end
end

正如我所指出的,它在直接比较时有效(例如,one_object == another_object),但my_array_of_objs.include? included_obj 总是返回false

【问题讨论】:

  • 当你提到直接比较对象时,我假设你的意思是在 model1 == model2 中。 my_array_of_models.include 吗?当您通过脚本/控制台尝试时,other_model 会按预期工作吗?
  • 不,Array.include?无论如何,在我的测试中总是返回 false ——即使是通过脚本/控制台。
  • Hmm.. 这里肯定有其他事情可以通过脚本/控制台进行快速测试,我在 ActiveRecord 子类上覆盖 == 以始终返回真正的线索到 Array.include?只要数组包含 1 个具有覆盖 == 的类型的模型,则始终返回 true。你能发布你的具体 == 实现吗?
  • .include 的结果?可能会有所不同,具体取决于它是 a == b 还是 b == a,如果这些对象中只有一个对象覆盖了该方法。我建议你做my_arrays_of_objs.map(&amp;:email).include? obj.email 之类的事情。以与预期行为截然不同的方式覆盖方法将非常不利于代码的可读性。

标签: ruby-on-rails ruby activerecord


【解决方案1】:

也许阅读这段文档很有启发性:

http://ruby-doc.org/core/classes/Object.html#M000341

array_of_models.include?(my_object) 可能不起作用,因为== 不用于测试集合是否有对象。它使用equal?

编辑

OP 断言即使他为他的模型覆盖 == 方法,array.include?(obj) 返回 false 也是不正确的。让我们看看这个:

class Event < ActiveRecord::Base
  def ==(that)
    if(that.eventname == self.eventname)
      true
    else
      false
   end
 end
end
>> a << Event.find(1)
=> [#<Event id: 1, eventname: "hemant", foo: nil, created_at: "2009-06-24 21:33:00", updated_at: "2009-06-24 21:33:00">]

>> b = Event.find(2)
=> #<Event id: 2, eventname: "hemant", foo: nil, created_at: "2009-06-24 21:33:04", updated_at: "2009-06-24 21:33:04">

>> a.include?(b)
=> true

显然这不是真的。但无论如何,我认为由于 AR 以特定方式定义 ==,因此覆盖它不是一个好主意。这可能会导致难以检测到错误。

【讨论】:

  • Array.include 的文档?说它使用“==”:ruby-doc.org/core/classes/Array.html#M002226
  • 确实如此。我想知道为什么你的模型对象数组返回 false (这导致我的结论有些错误)。
  • Array.include? 确实 返回 false,即使在适当的类上覆盖 == 也是如此。这不像我在说谎。 ;)
  • 不撒谎的朋友。但只是其他一些魔法正在发生,可能是您的插件之一或其他东西。
  • 我没有使用任何 Rails 插件。
【解决方案2】:

陈述问题的另一种方式可能是“== 方法是否被认为是'最终'?”。在 Java 中,您可以使用“final”关键字来防止方法在子类中被覆盖(您也可以将 final 应用于类,因此它们根本不能被子类化)。这在 OO 设计中可能是一件很方便的事情,因为某些类或方法并非旨在被覆盖。当然,Ruby 没有“final”,但同样的概念可能也适用于此处(也许您甚至可以使用元编程来实现它)。

我记得读过一些关于在 OO 中使用“final”的好文章和/或 SO 帖子。如果我找到它们,我会编辑它们。

【讨论】:

    【解决方案3】:

    ActiveRecord 模型是一个实体对象。按照设计,它代表数据库中的一行,id 列是在数据库级别唯一标识它的主键。

    正如 Hemant Kumar 指出的那样,您可以覆盖它,但这样做会使具有不同 id 但相同属性的对象被视为相等,这是不正确的。

    同样随着这一变化,设计从 ActiveRecord 实体对象转变为纯值对象。

    您的问题的可能解决方案是:

    Rails 故意将相等检查委托给标识列。 如果您想知道两个 AR 对象是否包含相同的东西,请比较 在两者上调用#attributes 的结果。

    https://stackoverflow.com/a/4738485/2987689

    这个问题还有一个好办法(https://stackoverflow.com/a/7098377/2987689

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-01-15
      • 2010-09-27
      • 1970-01-01
      • 1970-01-01
      • 2012-08-12
      • 1970-01-01
      • 2011-02-22
      • 1970-01-01
      相关资源
      最近更新 更多