1

在编写带有大量实例变量的大类时,编写 ==, eql? 和哈希方法是一个很大的麻烦。有没有办法制作一个“模板类”来自动化这个过程?或者用任何其他方式。

例子:

class Template
    def ==(other)
      //Some way of comparing all of self's instance variables to other's.
    end

    def eql?(other)
      self == other
    end

    def hash
      //Some way of XORing the instance variables of self
    end
end

class Test < Example
    attr_reader :foo
    attr_reader :bar

    def initialize(foo, bar)
      @foo = foo
      @bar = bar
    end
end

a = Test.new "123", "456"
b = Test.new "123", "456"
a == b
> true
4

3 回答 3

2
Test = Struct.new(:foo, :bar)

a = Test.new "123", "456"
b = Test.new "123", "456"

a == b
# => true
于 2013-09-06T17:18:55.410 回答
1

您可以定义您的字段,以便以后能够对其进行反思。假设所有实例变量始终存在并且您希望以类似的方式使用它们,Ruby 已经为您提供了足够的反射来实现它,大约是这种方式

class MyFineClass
  attr_reader :foo, :bar # and loads of stuff

  def initialize(...)
    # tons of those ivars initialized
  end

  def ==(other)
    other.is_a?(self.class) &&
      # this assumes all instance variables to have attr_readers
      instance_variables.all? { |ivar| send(ivar) == other.send(ivar) }
      # ...and if they don't, you need instance_variable_get, like this
      #instance_variables.all? { |ivar| instance_variable_get(ivar) == other.instance_variable_get(ivar) }
  end
end

如果您想更好地控制字段的处理方式,您可以添加“字段”的概念和一些元编程

class MyFineClass
  @fields = []
  def self.fields; @fields; end

  def self.fields(field_name)
    attr_reader field_name
    @fields << field_name
  end

  field :foo
  field :bar
  # and loads more

  def initialize(...)
    # again, tons of those ivars initialized
  end

  def ==(other)
    other.is_a?(self.class) && self.class.fields.all? { |f| send(f) == other.send(f) }
  end
end

接下来,您当然会拉取fields东西并将==模块分开并将其包含到MyFineClass. 明白了吗?进一步开发该模块,它可能开始看起来有点像ActiveModel或中的某些位DataMapper。:-)

于 2013-09-06T18:08:01.573 回答
0

Ruby Facets提供了Equitable模块,它几乎提供了您正在寻找的东西。

您的示例将变为:

class Test
  include Equitable(:foo, :bar)
end

如果您不想使用整个宝石,您可以抓住- 它很轻。

于 2014-11-08T22:15:00.630 回答