以下,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"
有没有办法引用这些方法?
答案 0 :(得分:4)
您遇到的问题是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
是继承)。
答案 1 :(得分:3)
您可以为此使用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#ancestors,Array#index,Integer#times,Enumerable#reduce(又名inject
),Object#method和Method#call。 / p>
E
和F
当然可以包含该类实例所需的其他实例方法。