簡體   English   中英

如何解決Ruby模塊/混合方法沖突

[英]How to resolve ruby module/mixin method conflict

在下面的代碼中, A繼承了F ,后者繼承了E ,因此在A實例上調用initialize調用A#initialize ,它優先於E#initializeF#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

我需要在A#initialize的方法體內同時引用E#initializeF#initialize ,分別將ef用作參數,以便得到以下結果:

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

有沒有辦法引用這些方法?

您遇到的問題是Ruby中沒有多重繼承。 因此,將模塊作為祖先插入,而使F#initialize覆蓋E#initialize 如您所見,使用super(f)可以很容易地訪問F#initialize 但是另一個需要黑客才能訪問:我們可以直接從模塊中選擇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是繼承)。

您可以為此使用Method#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] 

另請參見Module#ancestorsArray#indexInteger#timesEnumerable#reduce (aka inject ), Object#methodMethod#call

EF當然可以包含該類實例所需的其他實例方法。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM