2008-10-01 86 views
344

只是让我的头在Ruby元编程。 mixin /模块总是会让我困惑。Ruby中的include和extend有什么区别?

  • 包括:在指定的模块的方法混合在目标类
  • 实例方法延伸:在指定的模块的方法混合在目标类类方法

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

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

查看这个链接:http://juixe.com/techknow/index.php/2006/06/15/mixins-in-ruby/ – Donato 2016-12-20 17:53:00

回答

209

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

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

13

这是正确的。

在幕后,包括实际上是别名append_features,这(从文档):

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

274

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

  • 如果你打电话Klazz.extend(Mod),现在Klazz有国防部的方法(类方法)
  • 如果你打电话obj.extend(Mod),现在OBJ有国防部的方法(例如方法),但的obj.class没有其他的实例有那些方法添加。
  • extend是一个公共方法

包括 - 默认情况下,它在指定的模块的方法作为目标模块/类的实例方法混合。 例如

  • 如果你打电话class Klazz; include Mod; end;,现在Klazz的所有实例访问国防部的方法(例如方法)
  • include是一个私有方法,因为它旨在从容器类/模块中调用。

然而,模块非常经常由猴子修补included方法倍率include的行为。这在传统的Rails代码中非常突出。 more details from Yehuda Katz

include,其默认行为的更多细节,假设你运行下面的代码

class Klazz 
    include Mod 
end 
  • 如果国防部已经包含在Klazz,或其祖先之一,包括语句没有效果
  • 它还包括Mod在Klazz中的常量,只要它们不冲突
  • 它可以让Klazz访问Mod的模块变量,例如@@foo@@bar
  • 引发ArgumentError如果有环状包括
  • 将已模块作为呼叫者的直接祖先(即它增加了国防部到Klazz.ancestors,但国防部不被添加到Klazz.superclass.superclass.superclass的链所以在Klazz#foo中调用super将在检查Klazz的真正超类的foo方法之前检查Mod#foo,详情请参阅RubySpec。)。

当然,the ruby core documentation永远是这些事情的最佳去处。 The RubySpec project也是一个很好的资源,因为他们精确地记录了功能。

+13

我知道这是很旧的帖子,但答复的清晰度无法阻止我评论。非常感谢一个很好的解释。 – MohamedSanaulla 2011-12-25 15:30:07

4

所有其他的答案是好的,包括端通过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将扩展为(复制)到包含类中。

1

我以前学过它,但在使用它时非常感谢。这里的区别是:

这并不工作,但会工作,如果我把它定义为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 
1

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

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

class A 
include MyMOd 
end 

a = A.new 
a.some_method 

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

当我们使用extend时,我们正在向对象的特征类中的模块添加链接。 所以如果我们使用A.new.extend(MyMod),我们将模块添加到A的实例特征类或a'类。 如果我们使用A.extend(MyMod),我们正在向A添加链接(对象的类也是对象)本征类A'

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

也有一个前置方法,其改变查找路径:

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

对不起,我的英文不好。

相关问题