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

只是让我的头在Ruby元编程。 mixin /模块总是设法混淆我。

  • 包括 :在指定的模块方法中混合使用目标类中的实例方法
  • 扩展 :将指定的模块方法混合为目标类中的类方法

那么这个主要区别就在这个还是一个更大的潜伏龙? 例如

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

你所说的是正确的。 但是,除此之外还有更多。

如果你有一个类Klazz和模块Mod ,包括Klazz ModKlazz访问Mod的方法的实例。 或者你可以用Mod扩展Klazz ,让Klazz 访问Mod的方法。 但是你也可以用o.extend Mod扩展一个任意的对象。 在这种情况下,即使所有其他与o类相同的对象都没有,单个对象也可以获得Mod的方法。

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

  • 如果你叫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代码中非常突出。 来自耶胡达卡茨的更多细节 。

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

 class Klazz include Mod end 
  • 如果Mod已经包含在Klazz或其祖先之一中,则include语句不起作用
  • 它还包括Mod在Klazz中的常量,只要不冲突
  • 它让Klazz访问Mod的模块variables,例如@@foo@@bar
  • 如果存在循环包含,则引发参数错误
  • 附加模块作为调用者的直接祖先(即它将Mod添加到Klazz.ancestors中,但Mod不会添加到Klazz.superclass.superclass.superclass的链中。因此,在Klazz#foo中调用super将检查Mod#foo检查Klazz的真正的超类的foo方法,详见RubySpec)。

当然, Ruby核心文档总是最好的地方去做这些事情。 RubySpec项目也是一个很好的资源,因为他们精确地logging了function。

  • #include RubySpec rubydoc
  • #included RubySpec rubydoc
  • #extend RubySpec rubydoc
  • #extended RubySpec rubydoc
  • #extend_object RubySpec rubydoc
  • #append_features RubySpec rubydoc

这是正确的。

在幕后,include实际上是append_features的一个别名,它来自文档:

Ruby的默认实现是将此模块的常量,方法和模块variables添加到aModule,如果这个模块尚未添加到模块或其祖先之一。

所有其他答案都很好,包括通过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,则将复制方法和常量。 显然,如果你不小心,可以通过dynamic复制定义来浪费大量内存。

如果您使用ActiveSupport :: Concern,则.included()function可让您直接重写包含类。 关注模块中的模块ClassMethods被扩展 (复制)到包含类中。

我以前学过,但在使用它的时候很感激。 以下是区别:

这不起作用,但如果我已将其定义为def page_views(campaign)

 class UserAction include Calculations def self.page_views(campaign) overall_profit = calculate_campaign_profit(campaign) end end 

这工作:

 class UserAction extend Calculations def self.page_views(campaign) overall_profit = calculate_campaign_profit(campaign) end end 

我也想解释一下它的工作机制。 如果我不对,请纠正。

当我们使用include我们正在从我们的类添加一个链接到一个包含一些方法的模块。

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

对象没有方法,只有类和模块。 所以当a接收消息some_method它开始search方法some_methoda特征类,然后在A类,然后链接到A类模块,如果有一些(相反的顺序,最后包括胜)。

当我们使用extend我们正在向对象的特征类中的模块添加链接。 因此,如果我们使用A.new.extend(MyMod),我们将模块添加到A的实例特征类或a'类”。 如果我们使用A.extend(MyMod),我们正在给A(对象的,类也是对象)添加联系人A'征类A'

所以方法的查找path如下:a => a'=>将模块链接到一个'class => A

还有一个改变查找path的前置方法:

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

对不起,我的英语不好。