我最近学会了如何通过模块实现继承。但是,如果我想实现多重继承,我看不到干净的解决方案。 这是使用单继承并调用超类初始化方法的原始示例。:
module ModuleB
def initialize
puts "initialize from ModuleB"
@b = 5
end
end
module ModuleA
include ModuleB
def initialize
super
puts "initialize from ModuleA"
@a = @b
end
def action_1
@a = @b + 1
end
end
class ClassA
include ModuleA
def initialize
super
puts 'initialize - method in ClassA'
@c = @a
@d = @b
puts "a = #@a"
puts "b = #@b"
puts "c = #@c"
puts "d = #@d"
end
end
instA = ClassA.new
puts instA.action_1
但是,如果ClassA中包含额外的ModuleC,我们如何调用“超类”来初始化mehods?
module ModuleB
def initialize
puts "initialize from ModuleB"
@b = 5
end
end
module ModuleA
include ModuleB
def initialize
super
puts "initialize from ModuleA"
@a = @b
end
def action_1
@a = @b+1
@a = @b + 1
end
end
module ModuleC
include ModuleB
def initialize
super
puts "initialize from ModuleC"
@a = 'cute Zuzia'
end
def action_1
@a = 'something'
end
end
class ClassA
include ModuleA
include ModuleC
def initialize
super # which initialize will be called? From moduleA or from moduleC?
# what if I wanted to invoke ModuleC initialize from classA instance as well?
puts 'initialize - method in ClassA'
@c = @a
@d = @b
puts "a = #@a"
puts "b = #@b"
puts "c = #@c"
puts "d = #@d"
end
end
在这种情况下如何使用mixin启用多重继承?
答案 0 :(得分:3)
将调用哪个初始化?来自moduleA还是来自moduleC?
class C
include M
end
非常简单:它使M
的超类C
和C
的超类M
的超类成为M
的超类。换句话说,它只不过是标准的无聊的旧类继承。 (更确切地说:它创建了一个类,它与C
共享其方法表指针,常量表指针和类变量表指针,并使该类成为C
的超类和前一个超类{ {1}}这个新创建的类的超类。它还检查M
的祖先是否已经在C
的祖先链中,在这种情况下它什么都不做。)
module M1
include M2
end
显然,这不能使M2
成为M1
的超类,因为模块没有超类。它所做的只是记录M2
是M1
的祖先这一事实,以便将来M1
include
成为一个类和一个为M1
创建了一个类,M2
将包含在这个新创建的类中。
所以,它只是类继承。简单,无聊,类继承。
让我们看一下代码中的情况:
module ModuleB; end
好的,我们已经定义了一个名为ModuleB
的模块。
module ModuleA
include ModuleB
end
我们已经定义了一个名为ModuleA
的模块,它在其中的某个位置存储了这样一个事实:当include
进入某个类时,ModuleB
应该是include
也是。 (让我们假设有一个实例变量@__included_modules__
或类似的东西,而Module#include
只是实现为@__included_modules__ << m
。)
module ModuleC
include ModuleB
end
我们再次定义了一个名为ModuleC
的模块,它在其中的某个位置存储了这样一个事实:当include
进入类时,ModuleB
应该是{{1}也是。
include
让我们一步一步。
class ClassA
include ModuleA
include ModuleC
end
的超类是ClassA
(如果没有明确指定,则为隐式超类)。Object
首先检查include ModuleA
的祖先链中是否已ModuleA
。它不是。ClassA
)。〚ModuleA′〛
(Object
的当前超类)成为ClassA
〚ModuleA′〛
成为〚ModuleA′〛
的超类。此时,祖先链如下所示:ClassA
→ClassA
→〚ModuleA′〛
→Object
→Kernel
BasicObject
中记录的每个模块的所有内容。ModuleA
的祖先是否已经在ModuleB
的祖先链中。它不是。ClassA
〚ModuleB′〛
的当前超类,当然〚ModuleA′〛
是Object
的超类。〚ModuleB′〛
成为〚ModuleB′〛
的超类。此时,祖先链如下所示:〚ModuleA′〛
→ClassA
→〚ModuleA′〛
→〚ModuleB′〛
→Object
→Kernel
。BasicObject
行并继续include ModuleA
。同样,它只是一样的简单步骤:include ModuleC
首先检查include ModuleC
的祖先链中是否已ModuleC
。它不是。ClassA
)。〚ModuleC′〛
(〚ModuleA′〛
的当前超类)成为ClassA
〚ModuleC′〛
成为〚ModuleC′〛
的超类。此时,祖先链如下所示:ClassA
→ClassA
→〚ModuleC′〛
→〚ModuleA′〛
→〚ModuleB′〛
→Object
→{{1 }} Kernel
中记录的每个模块的所有内容。BasicObject
的祖先是否已经在ModuleC
的祖先链中。它是。所以它不会再次获得ModuleB
。 ClassA
的祖先链如下所示:include
→ClassA
→ClassA
→〚ModuleC′〛
→〚ModuleA′〛
→{ {1}}→〚ModuleB′〛
。
所以,重复一下你的问题:
将调用哪个初始化?来自moduleA还是来自moduleC?
Object
只需要一步一步提升&#34;祖先链。由于我们在Kernel
,BasicObject
将走向祖先链,从super
的超类开始,直到找到另一个具有相同名称的方法。因此,它将从ClassA#initialize
开始,它确实找到super
方法并运行它。
嗯,那是我们刚刚做的,不是吗?只需致电如果我想从
ClassA
实例调用〚ModuleC′〛
该怎么办?
initialize
。
或者你的意思是没有还打电话给ModuleC#initialize
?在这种情况下,您可以做的是直接抓住classA
并将其绑定到super
:
ClassA#initialize
我知道很多Ruby教程都会让所有这些听起来非常复杂。但它不是。 ModuleC#initialize
使得包含在其中包含的任何内容的超类的模块。期。它真的 简单。 Mixin继承只是继承。其他一切,方法查找,常量查找,instA
,无论如何,它只是一样。
答案 1 :(得分:1)
要回答“将调用哪个初始化?来自moduleA或来自moduleC?”的问题,它是intialize
module
的{{1}} - 最后一个。这是Ruby中的一般规则。
你的例子确实让我思考了一下,即使你这样做只是为了好玩。我对如何实现你想要的东西略有不同。
1)创建一个include
共有的模块,用于定义执行module
方法工作的proc
。这里的一个缺点是我正在改变常量。
initialize
2。)与上面类似,但避免常数。尽管如此,仍然使用module C
INIT = []
end
module A
include C
INIT << ->{puts "initialize() of Module A"}
end
module B
include C
INIT << ->{puts "initialize() of Module B"}
end
class D
include A
include B
def initialize
# Note that you don't need the 'C::' part here,
# but it is safer.
C::INIT.each(&:call)
puts "initialize of class D"
end
end
d = D.new
。
proc
顺便说一下,“在以后执行module C
@@initializers = []
def add_initializer(proc)
@@initializers << proc
end
def initializers
@@initializers
end
end
module A
extend C
add_initializer(->{puts "initialize() of Module A"})
end
module B
extend C
add_initializer(->{puts "initialize() of Module B"})
end
class D
include A
include B
include C
def initialize
initializers.each(&:call)
puts "initialize of class D"
end
end
d = D.new
s的注册代码”模式在Rails中非常突出。
修改强>
如果你想做一些比在proc
体内添加puts
更复杂的事情,你可以在proc
的体内调用一个方法提升你想要的。
我已将proc
的定义更改为:
module A