Ruby类层次结构中'prepend'的行为
我有一个类Ruby类层次结构中'prepend'的行为,ruby,inheritance,decorator,Ruby,Inheritance,Decorator,我有一个类Base,还有两个类Derived和Derived2继承自Base。它们各自定义了一个函数foo 我还有一个模块Gen,它是prepend-ed到Base。它也是前置的-ed到Derived2,而不是派生的 当我对Derived2的一个实例调用foo时,结果就好像Gen模块只prepend-ed到Base而不是Derived2一样。这是预期的行为吗 以下是上述场景的代码: module Gen def foo val = super '[' + val + ']'
Base
,还有两个类Derived
和Derived2
继承自Base
。它们各自定义了一个函数foo
我还有一个模块Gen
,它是prepend
-ed到Base
。它也是前置的-ed到Derived2
,而不是派生的
当我对Derived2
的一个实例调用foo
时,结果就好像Gen
模块只prepend
-ed到Base
而不是Derived2
一样。这是预期的行为吗
以下是上述场景的代码:
module Gen
def foo
val = super
'[' + val + ']'
end
end
class Base
prepend Gen
def foo
"from Base"
end
end
class Derived < Base
def foo
val = super
val + "from Derived"
end
end
class Derived2 < Base
prepend Gen
def foo
val = super
val + "from Derived"
end
end
Base.new.foo # => "[from Base]"
Derived.new.foo # => "[from Base]from Derived"
Derived2.new.foo # => "[from Base]from Derived"
为了帮助您理解,有一个方法类#祖先
,它告诉您搜索方法的顺序。在这种情况下:
Base.ancestors # => [Gen, Base, Object, Kernel, BasicObject]
Derived.ancestors # => [Derived, Gen, Base, Object, Kernel, BasicObject]
Derived2.ancestors # => [Gen, Derived2, Gen, Base, Object, Kernel, BasicObject]
因此,当您对作为所述类的实例的对象调用方法时,将按照该顺序在相应列表中搜索该方法
- 前置将模块置于该列表的前面
- 继承将父链放在子链的末尾(不完全是这样,但为了简单起见)
super
只是说“继续遍历链,找到相同的方法”
对于Base
,我们有两个foo
的实现,一个在Base
中,另一个在Gen
中。将首先找到Gen
模块,因为模块已预先准备好。因此,在Base
实例上调用它将调用Gen#foo
=[S]
,这也将向上搜索链(通过super
)=[from Base]
对于派生的
,模块没有前缀,我们有继承。因此,第一个发现的实现是,在Derived
=Sfrom-Derived
和super
中,将搜索来自Base
的链的其余部分(也就是上面的段落被播放出来)=[from Base]from-Derived
对于Derived2
,模块是预先设置的,因此将首先找到那里的方法=[S]
。然后那里的super
将在Derived2
=[Sfrom-Derived]
中找到下一个foo
,那里的super
将再次播放Base
的情况
编辑:似乎直到最近,prepend
才会首先在祖先链中搜索,并仅在模块尚未存在时添加模块(类似于包含
)。更令人困惑的是,如果您首先创建父级,从父级继承,在子级中预加,然后在父级中预加,您将得到更新版本的结果。我知道预加
应该如何工作。但是祖先链不是你上面印的。1. Base.祖先#=>[Gen,Base,Object,Kernel,BasicObject]
2<代码>派生的.祖先#=>[派生的,根,基,对象,内核,基本对象]
3Derived2.祖先#=>[Derived2,Gen,Base,Object,Kernel,BasicObject]
我现在使用的是Ruby 2.2.4。你知道这种行为改变是在哪个版本发生的吗?@CppNoob我现在下载2.2.5,但它似乎在2.3+之后发生了变化。它没有反映在文档中,我在变更日志中也找不到它。这似乎是一个回归,我会做更多的调查并报告。@CppNoob,是的-它是2.3。0@CppNoob,这是2.3.0之前的“正常”行为。[Gen,Derived2,Gen,Base,Object,Kernel,BasicObject]
版本附带了2.3.0版本。明确声明,如果它已经存在于链中,则不应预先添加它。所以这似乎是回归,我创造的。
Base.ancestors # => [Gen, Base, Object, Kernel, BasicObject]
Derived.ancestors # => [Derived, Gen, Base, Object, Kernel, BasicObject]
Derived2.ancestors # => [Gen, Derived2, Gen, Base, Object, Kernel, BasicObject]