Как разрешить конфликт между модулем ruby ​​и методом mixin - PullRequest
0 голосов
/ 18 января 2019

В дальнейшем A наследует F, который наследует E, поэтому вызов initialize для экземпляра A вызывает A#initialize, который имеет приоритет над E#initialize и F#initialize.

module E
  def initialize(e)
    @e = e
  end

  def e
    @e
  end
end

module F
  def initialize(f)
    @f = f
  end

  def f
    @f
  end
end

class A
  include E
  include F

  def initialize(e, f)
    # ...
  end
end

Мне нужно сослаться на E#initialize и F#initialize в теле метода A#initialize, передавая e и f соответственно в качестве аргумента, чтобы я получил такой результат:

a = A.new("foo", "bar")
a.e # => "foo"
a.f # => "bar"

Есть ли способ обратиться к этим методам?

Ответы [ 2 ]

0 голосов
/ 18 января 2019

Для этого можно использовать Метод # super_method .

module E
  def initialize(e)
    @e = e
  end
  def e
    @e
  end
end

module F
  def initialize(f)
    @f = f
  end

  def f
    @f
  end
end

class A
  include E
  include F

  def initialize(e, f)
    select_initialize(E).call e
    select_initialize(F).call f
  end

  private

  def select_initialize(mod) 
    self.class.
         ancestors.
         index(mod).
         times.
         reduce(method(:initialize)) { |m,_| m.super_method }
  end
end

puts A.new("E", "F").f
  #=> F
puts A.new("E", "F").e
  #=> E

Примечание:

A.ancestors
  #=> [A, F, E, Object, Kernel, BasicObject] 

См. Также Модуль # предков , Массив # index , Целое число # раз , Перечислимый # уменьшить (он же inject), Object # method и Method # call .

E и F могут, конечно, содержать другие методы экземпляра, которые необходимы экземплярам класса.

0 голосов
/ 18 января 2019

Проблема для вас в том, что в Ruby нет множественного наследования; поэтому модули вставляются как предки один над другим, и поэтому F#initialize затмевает E#initialize. Как вы обнаружили, к F#initialize легко получить доступ super(f); но другому нужен взлом для доступа: мы можем выбрать метод initialize непосредственно из модуля, потому что он является предком; затем привязать его к текущему объекту и запустить его.

def initialize(e, f)
  E.instance_method(:initialize).bind(self).call(e)
  F.instance_method(:initialize).bind(self).call(f) # equivalent to super(f)
end

Однако я не рекомендую это. Если вам нужно запустить несколько инициализаторов, вам лучше использовать композицию, а не наследование (и, надо понимать, include - это наследование).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...