在下文中,A
继承了F
,继承了E
,因此在A
实例上调用initialize
会调用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
我需要从 A#initialize
的方法主体中引用 E#initialize
和 F#initialize
,分别传递 e
和 f
作为参数,以便我得到这个结果:
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]
另请参阅模块#祖先,数组#索引,整数#时间,枚举#reduce(又名inject
),对象#方法和方法#调用。
当然,E
和F
可能包含类实例所需的其他实例方法。