覆盖ActiveRecord子类中的'=='方法有意义吗?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了覆盖ActiveRecord子类中的'=='方法有意义吗?相关的知识,希望对你有一定的参考价值。

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之间的桥梁(就像社交网络中的“朋友”关系),如果他们有相同的true应该返回user_id

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

答案

阅读这篇文档也许是有益的:

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以特定的方式定义了==,因此覆盖它并不是一个好主意。它可能导致难以检测到错误。

另一答案

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

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

另一答案

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

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

此外,通过此更改,设计从ActiveRecord实体对象转移到纯值对象。

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

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

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

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

以上是关于覆盖ActiveRecord子类中的'=='方法有意义吗?的主要内容,如果未能解决你的问题,请参考以下文章

仅列出 ActiveRecord 子类方法

覆盖 ActiveRecord 属性方法

yii2之ActiveRecord 模型

覆盖子类中的 Qt 插槽

父类中的方法被覆盖以及子类调用父类覆盖的方法

php 检查子类中的方法是不是被覆盖