【问题标题】:Behaviour of `prepend` in Ruby class hierarchiesRuby 类层次结构中“前置”的行为
【发布时间】:2017-02-22 03:51:06
【问题描述】:

我有一个类Base,还有两个类DerivedDerived2 继承自Base。它们每个都定义了一个函数foo

我还有一个模块Gen,它是prepend-ed 到Base。它也是prepend-ed 到Derived2 但不是Derived

当我在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"

我预计上述语句的最后一个输出:

[[from Base]from Derived]

【问题讨论】:

    标签: ruby inheritance decorator


    【解决方案1】:

    为了帮助您理解,有一个方法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]
    

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

    • 前置会将模块置于该列表的前面。
    • 继承将父链放在子链的末尾(不完全是,但为了简单起见)。
    • super 只是说“去更远的链中找到我同样的方法”

    对于Base,我们有两个foo 的实现——在BaseGenGen 将首先找到,因为该模块是预先添加的。因此在Base 的实例上调用它会调用Gen#foo =[S],它也会向上搜索链(通过super=[from Base] em>。

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

    对于Derived2,模块是前置的,所以会先找到那里的方法=[S]。然后那里的super 会在Derived2 中找到下一个foo =[Sfrom Derived],而那里的super 将再次播放Base 的情况=@ 987654350@.


    编辑:似乎直到最近,prepend 才会首先在祖先链中搜索并仅在模块不存在时才添加该模块(类似于include)。更令人困惑的是,如果您首先创建父级,从其继承,在子级中添加前缀,然后在父级中添加前缀,您将获得较新版本的结果。

    【讨论】:

    • 我知道prepend 应该如何工作。但是祖先链并不是你上面打印的。 1.Base.ancestors # =&gt; [Gen, Base, Object, Kernel, BasicObject] 2.Derived.ancestors # =&gt; [Derived, Gen, Base, Object, Kernel, BasicObject] 3.Derived2.ancestors # =&gt; [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。 documentation 明确指出,如果它已经存在于链中,则不应添加它。所以好像是回归,我创建了a ticket
    猜你喜欢
    • 2013-09-17
    • 1970-01-01
    • 2013-12-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多