首页 文章

Ruby中包含和扩展有什么区别?

提问于
浏览
383

刚刚开始关注Ruby元编程 . mixin / modules总是让我困惑 .

  • include :在目标类中将指定的模块方法混合为 instance methods

  • extend :在目标类中将指定的模块方法混合为 class methods

那么主要区别在于这还是潜伏着更大的龙?例如

module ReusableModule
  def module_method
    puts "Module Method: Hi there!"
  end
end

class ClassThatIncludes
  include ReusableModule
end
class ClassThatExtends
  extend ReusableModule
end

puts "Include"
ClassThatIncludes.new.module_method       # "Module Method: Hi there!"
puts "Extend"
ClassThatExtends.module_method            # "Module Method: Hi there!"

5 回答

  • 227

    你所说的是对的 . 然而,除此之外还有更多 .

    如果你有一个类 Klazz 和模块 Mod ,包括 Mod 中的 Mod 给出了 Klazz 访问 Mod 方法的实例 . 或者您可以使用 Mod 扩展 Klazz ,使 Klazz 类可以访问 Mod 的方法 . 但您也可以使用 o.extend Mod 扩展任意对象 . 在这种情况下,单个对象获取 Mod 的方法,即使与 o 具有相同类的所有其他对象也不会 .

  • 1

    extend - 添加指定的模块's methods and constants to the target' s元类(即单例类),例如

    • 如果你调用 Klazz.extend(Mod) ,现在Klazz有Mod的方法(作为类方法)

    • 如果你调用 obj.extend(Mod) ,现在obj有Mod的方法(作为实例方法),但 obj.class 的其他实例没有添加这些方法 .

    • extend 是一种公共方法

    include - 默认情况下,它将指定模块的方法混合为目标模块/类中的实例方法 . 例如

    • 如果你调用 class Klazz; include Mod; end; ,现在Klazz的所有实例都可以访问Mod的方法(作为实例方法)

    • include 是一个私有方法,因为它旨在从容器类/模块中调用 .

    However ,模块经常通过猴子修补 included 方法来覆盖 include 的行为 . 这在传统的Rails代码中非常突出 . more details from Yehuda Katz .

    假设您运行以下代码,有关 include 及其默认行为的更多详细信息

    class Klazz
      include Mod
    end
    
    • 如果Mod已包含在Klazz或其祖先之一中,则include语句无效

    • 它还包括Mod 's constants in Klazz, as long as they don' t冲突

    • 它使Klazz能够访问Mod的模块变量,例如 @@foo@@bar
      如果存在循环包含,

    • 会引发ArgumentError

    • 将模块作为调用者的直接祖先附加(即它将Mod添加到Klazz.ancestors,但是Mod没有添加到Klazz.superclass.superclass.superclass的链中 . 所以,在Klazz#foo中调用 super 将检查Mod#foo在检查Klazz 's real superclass' s foo方法之前 . 有关详细信息,请参阅RubySpec .

    当然,the ruby core documentation总是最适合这些事情的地方 . The RubySpec project也是一个很棒的资源,因为他们准确地记录了这些功能 .

  • 13

    那是对的 .

    在幕后,include实际上是 append_features 的别名,(来自文档):

    Ruby的默认实现是将此模块的常量,方法和模块变量添加到aModule(如果此模块尚未添加到aModule或其祖先之一) .

  • 301

    所有其他答案都很好,包括挖掘RubySpecs的提示:

    https://github.com/rubyspec/rubyspec/blob/master/core/module/include_spec.rb

    https://github.com/rubyspec/rubyspec/blob/master/core/module/extend_object_spec.rb

    至于用例:

    如果在ClassThatIncludes类中包含模块ReusableModule,则会引用方法,常量,类,子模块和其他声明 .

    如果使用模块ReusableModule扩展类ClassThatExtends,则会复制方法和常量 . 显然,如果你不小心,你可以通过动态复制定义来浪费大量内存 .

    如果使用ActiveSupport :: Concern,则.included()功能允许您直接重写包含类 . 关注内的模块ClassMethods被扩展(复制)到包含类中 .

  • 4

    我还想解释它的作用机制 . 如果我不对,请纠正 .

    当我们使用 include 时,我们将从类中添加一个链接到一个包含一些方法的模块 .

    class A
    include MyMOd
    end
    
    a = A.new
    a.some_method
    

    对象没有方法,只有clases和模块 . 所以当 a 收到消息 some_method 时,它会在 a 的本征类中开始搜索方法 some_method ,然后在 A 类中然后链接到 A 类模块(如果有的话)逆序,最后包括胜利) .

    当我们使用 extend 时,我们将对象's eigen class. So if we use A.new.extend(MyMod) we are adding linkage to our module to A' s实例特征类或 a' 类中的模块添加链接 . 如果我们使用A.extend(MyMod),我们将添加链接到A(对象,类也是对象)eigenclass A' .

    所以 a 的方法查找路径如下:a => a ' => linked modules to a' class => A.

    还有一个更改查找路径的prepend方法:

    a => a'=>前置模块A => A =>包含模块到A

    对不起,我的英语不好 .

相关问题