刚刚开始关注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 回答
你所说的是对的 . 然而,除此之外还有更多 .
如果你有一个类
Klazz
和模块Mod
,包括Mod
中的Mod
给出了Klazz
访问Mod
方法的实例 . 或者您可以使用Mod
扩展Klazz
,使Klazz
类可以访问Mod
的方法 . 但您也可以使用o.extend Mod
扩展任意对象 . 在这种情况下,单个对象获取Mod
的方法,即使与o
具有相同类的所有其他对象也不会 .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
及其默认行为的更多详细信息如果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也是一个很棒的资源,因为他们准确地记录了这些功能 .
#include
RubySpec rubydoc#included
RubySpec rubydoc#extend
RubySpec rubydoc#extended
RubySpec rubydoc#extend_object
RubySpec rubydoc#append_features
RubySpec rubydoc那是对的 .
在幕后,include实际上是 append_features 的别名,(来自文档):
所有其他答案都很好,包括挖掘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被扩展(复制)到包含类中 .
我还想解释它的作用机制 . 如果我不对,请纠正 .
当我们使用
include
时,我们将从类中添加一个链接到一个包含一些方法的模块 .对象没有方法,只有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(对象,类也是对象)eigenclassA'
.所以
a
的方法查找路径如下:a => a ' => linked modules to a' class => A.还有一个更改查找路径的prepend方法:
a => a'=>前置模块A => A =>包含模块到A
对不起,我的英语不好 .