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

刚刚开始关注Ruby元编程。 mixin / modules老是让我困惑。 html

  • include :将指定模块方法中的混合做为目标类中的实例方法
  • extend :将指定的模块方法混合为目标类中的类方法

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

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!"

#1楼

那是对的。 github

在幕后,include其实是append_features的别名,(来自文档): 编程

Ruby的默认实现是将此模块的常量,方法和模块变量添加到aModule(若是此模块还没有添加到aModule或其祖先之一)。 ruby


#2楼

你所说的是对的。 然而,除此以外还有更多。 app

若是你有一个类Klazz和模块Mod ,包括Klazz ModKlazzKlazz访问Mod的方法。 或者您可使用Mod扩展Klazz ,使Klazz Klazz访问Mod的方法。 可是你也能够用o.extend Mod扩展一个任意对象。 在这种状况下,单个对象获取Mod的方法,即便与o具备相同类的全部其余对象也没有。 spa


#3楼

我还想解释它的做用机制。 若是我不对,请纠正。 .net

当咱们使用include咱们将从类中添加一个连接到一个包含一些方法的模块。 code

class A
include MyMOd
end

a = A.new
a.some_method

对象没有方法,只有clases和模块。 因此当a接收消息some_method它开始在a特征类中搜索方法some_method ,而后在A类中而后连接到A类模块,若是有一些(以相反顺序,最后包括胜利)。 htm

当咱们使用extend咱们将对象的特征类中的模块添加连接。 所以,若是咱们使用A.new.extend(MyMod),咱们将模块的连接添加到A的实例特征类或a'类。 若是咱们使用A.extend(MyMod),咱们将添加连接到A(对象,类也是对象)本征类A'

所以对于方法查找路径a是以下:a =>一个“=>连接的模块为”类=> A.

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

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

对不起,个人英语很差。


#4楼

extend - 将指定模块的方法和常量添加到目标的元类(即单例类),例如

  • 若是你调用Klazz.extend(Mod) ,如今Klazz有Mod的方法(做为类方法)
  • 若是你调用obj.extend(Mod) ,如今obj有Mod的方法(做为实例方法),但没有其余obj.class实例添加了这些方法。
  • extend是一种公共方法

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

  • 若是你打电话给class Klazz; include Mod; end; class Klazz; include Mod; end; ,如今Klazz的全部实例均可以访问Mod的方法(做为实例方法)
  • include是一个私有方法,由于它是从容器类/模块中调用的。

可是 ,模块常常经过猴子修补included方法来覆盖 include的行为。 这在传统的Rails代码中很是突出。 来自Yehuda Katz的更多细节

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

class Klazz
  include Mod
end
  • 若是Mod已包含在Klazz或其祖先之一中,则include语句无效
  • 它还包括Mod在Klazz中的常量,只要它们不冲突
  • 它使Klazz能够访问Mod的模块变量,例如@@foo@@bar
  • 若是存在循环包含则引起ArgumentError
  • 将模块做为调用者的直接祖先附加(即它将Mod添加到Klazz.ancestors,可是Mod没有添加到Klazz.superclass.superclass.superclass的链中。因此,在Klazz #foo中调用super将检查Mod#foo以前检查Klazz真正的超类的foo方法。有关详细信息,请参阅RubySpec。)。

固然, ruby核心文档老是最适合这些事情的地方。 RubySpec项目也是一个很棒的资源,由于他们精确地记录了这些功能。


#5楼

全部其余答案都很好,包括挖掘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被扩展 (复制)到包含类中。

相关文章
相关标签/搜索