Ruby中的'include'和'prepend'有什么区别?

34

来自模块

Module#append_features(mod) → mod => 当此模块包含在另一个模块中时,Ruby会在此模块中调用append_features方法,将其传递给mod接收的模块。如果尚未将此模块添加到mod或其祖先之一中,则Ruby的默认实现是添加此模块的常量,方法和模块变量到mod中。

Module#prepend_features(mod) → mod => 当此模块在另一个模块中预定义时,Ruby会在此模块中调用prepend_features方法,并将其传递给mod接收的模块。如果尚未将此模块添加到mod或其祖先之一中,则Ruby的默认实现是覆盖此模块的常量,方法和模块变量到mod中。

有人能帮我理解以下问题吗:

  • 哪些Module的特性被定义为除了默认特性外的appendprepend

  • 它们在功能上有何区别?

  • 何时使用append_features,何时使用prepend_features

  • 以上两行(加粗)的区别是什么?

2个回答

43
  • Module的append和prepend定义了哪些特性?

正如您引用的文本中所指定的:

常量、方法和模块变量

  • 它们在功能上有何不同?

它们都将混入模块的方法添加到传递的模块(类)中。区别在于,如果目标类已经定义了这些方法,则它们的查找顺序不同:

include的行为就像目标类继承了混入的模块:

module FooBar
  def say
    puts "2 - Module"
  end
end

class Foo
  include FooBar

  def say
    puts "1 - Implementing Class"
    super
  end
end

Foo.new.say # =>
            # 1 - Implementing Class
            # 2 - Module

prepend 使混入模块的方法“更强”,并首先执行它们:

module FooBar
  def say
    puts "2 - Module"
    super
  end
end

class Foo
  prepend FooBar

  def say
    puts "1 - Implementing Class"
  end
end

Foo.new.say # =>
            # 2 - Module
            # 1 - Implementing Class

以下示例源自:http://blog.crowdint.com/2012/11/05/3-killer-features-that-are-coming-on-ruby-2-0.html

  • 何时使用append_features和何时使用prepend_features?

使用prepend当您想要将目标模块(类)的方法保持在方法查找链的末尾时。

一些真实世界的例子可以通过搜索SO(Stack Overflow)来查找rubymoduleprepend

(注意:我仅提到了方法,因为它们在继承和混入时最容易理解,但同样适用于其他特性。)


Ruby的默认实现是添加该模块的常量、方法和模块变量。这已经可以了。除此之外还有其他功能吗? - Arup Rakshit
你在这里给出了很好的解释。你能否看一下我的最后一个问题并更新第一个问题? - Arup Rakshit
  1. 只有常量、方法和模块变量,没有其他的。
  2. 本质上与2)相同,并通过示例进行解释。
- Mladen Jablanović
感谢您的努力和时间帮助我理解。 :) - Arup Rakshit

8

我本想将它作为评论添加到@Mladen Jablanovic已经发布的好答案中,但由于我的声望点数太低而无法实现。

我在这里找到了一篇更简洁、更清晰和更具描述性的文章 - Ruby模块:Include vs Prepend vs Extend,我在这里发布它,以防有人需要它并能用最少的努力获得它。

直接引用:

Though include is the most common way of importing external code into a class, Ruby provides also two other ways to achieve that: extend and prepend. However, they don’t have the same behavior at all, and these differences are often misunderstood by Ruby developers.

To understand how to use them, we must first have a deeper look into how Ruby is resolving methods to execute at runtime, using something called the ancestors chain.

When a Ruby class is created, it holds a list of constant names which are its ancestors. They are all the classes that the class inherits from, and the modules they include. For example, by calling ancestors on the String class, we get the list of its ancestors:

String.ancestors
=> [String, Comparable, Object, PP::ObjectMixin, Kernel, BasicObject]

include is the most used and the simplest way of importing module code. When calling it in a class definition, Ruby will insert the module into the ancestors chain of the class, just after its superclass.

Available since Ruby 2, prepend is a bit less known to Rubyists than its two other friends. It actually works like include, except that instead of inserting the module between the class and its superclass in the chain, it will insert it at the bottom of the chain, even before the class itself.

我建议阅读这篇文章,以便更好地理解,因为它附带有示例。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接