Как правильно реализовать равенство в ruby? - PullRequest
46 голосов
/ 19 декабря 2009

Для простого структурного класса:

class Tiger
  attr_accessor :name, :num_stripes
end

как правильно правильно реализовать равенство, чтобы гарантировать, что ==, ===, eql? и т. Д. Работают, и чтобы экземпляры класса хорошо играли в наборах, хешах и т. Д.

EDIT

Кроме того, каков хороший способ реализовать равенство, если вы хотите сравнить на основе состояния, которое не отображается вне класса? Например:

class Lady
  attr_accessor :name

  def initialize(age)
    @age = age
  end
end

здесь я бы хотел, чтобы мой метод равенства учитывал @age, но Леди не раскрывает свой возраст клиентам. Должен ли я использовать instance_variable_get в этой ситуации?

Ответы [ 3 ]

68 голосов
/ 26 декабря 2009

Чтобы упростить операторы сравнения для объектов с более чем одной переменной состояния, создайте метод, который возвращает все состояние объекта в виде массива. Затем просто сравните два состояния:

class Thing

  def initialize(a, b, c)
    @a = a
    @b = b
    @c = c
  end

  def ==(o)
    o.class == self.class && o.state == state
  end

  protected

  def state
    [@a, @b, @c]
  end

end

p Thing.new(1, 2, 3) == Thing.new(1, 2, 3)    # => true
p Thing.new(1, 2, 3) == Thing.new(1, 2, 4)    # => false

Кроме того, если вы хотите, чтобы экземпляры вашего класса можно было использовать в качестве хеш-ключа, добавьте:

  alias_method :eql?, :==

  def hash
    state.hash
  end

Они должны быть публичными.

18 голосов
/ 01 сентября 2014

Чтобы проверить все ваши переменные экземпляра сразу:

def ==(other)
  other.class == self.class && other.state == self.state
end

def state
  self.instance_variables.map { |variable| self.instance_variable_get variable }
end
1 голос
/ 19 декабря 2009

Обычно с оператором ==.

def == (other)
  if other.class == self.class
    @name == other.name && @num_stripes == other.num_stripes
  else
    false
  end
end
...