Ruby类层次结构中`prepend`的行为

时间:2016-10-13 06:38:55

标签: ruby inheritance decorator

我有一个班级Base,以及两个继承自Derived的班级Derived2Base。它们每个都有一个函数foo

我还有一个Gen模块prepend - 编辑为Base。它也是prepend - Derived2但不是Derived

当我在foo的实例上调用Derived2时,结果就好像Gen模块仅prepend - 编辑为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"

我期望输出上述语句的最后一个:

[[from Base]from Derived]

1 个答案:

答案 0 :(得分:3)

为了帮助您理解,有一种方法Class#ancestors,它告诉您搜索方法的顺序。在这种情况下:

Base.ancestors     # => [Gen, Base, Object, Kernel, BasicObject]
Derived.ancestors  # => [Derived, Gen, Base, Object, Kernel, BasicObject]
Derived2.ancestors # => [Gen, Derived2, Gen, Base, Object, Kernel, BasicObject]

因此,当您在作为所述类的实例的对象上调用方法时,将按相应的顺序在该对应的列表中搜索该方法。

  • Prepending将模块放在该列表的前面。
  • 继承将父级链放在子级的末尾(不完全是这样,但为了简单起见)。
  • super只是说“进一步遍历链条并找到相同的方法”

对于Base,我们有两个foo实现 - BaseGen。在模块被预先添加之前,将首先找到Gen。因此,在Base的实例上调用它会调用Gen#foo = [S] ,这也将搜索链(通过super)< EM> = [from Base]

对于Derived,模块没有前置,我们有继承。因此,第一个找到的实现是Derived = Sfrom Derived super将搜索来自Base的其他链(又名{播放上面的段落) = [from Base]from Derived

对于Derived2,模块是前置的,因此首先会找到 = [S] 的方法。然后,super会在foo = Derived2 中找到下一个[Sfrom Derived]super将会发现情况再次Base = [[from Base]from Derived]

编辑:似乎直到最近,prepend才首先在祖先链中进行搜索并仅在模块尚未存在时添加模块(类似于include })。为了让它更令人困惑,如果你第一次创建父项,从它继承,在子项之前添加,然后在父项之前,你将获得更新版本的结果。