我只是在学习Ruby元编程。mixin/modules总是让我感到困惑。
*包括:在目标类中混合指定的模块方法作为示例方法
*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!"
8条答案
按热度按时间4smxwvx51#
extend-将指定模块的方法和常量添加到目标的元类(即singleton类)。
Klazz.extend(Mod)
,现在Klazz有Mod的方法(作为类方法)obj.extend(Mod)
,现在obj有Mod的方法(作为示例方法),但是obj.class
的其他示例没有添加这些方法。extend
是一个公共方法include-默认情况下,它混合指定模块的方法作为目标模块/类中的示例方法。例如
class Klazz; include Mod; end;
,现在Klazz的所有示例都可以访问Mod的方法(作为示例方法)include
是一个私有方法,因为它是在容器类/模块中调用的。然而,模块经常通过对
included
方法进行猴子修补来 * 覆盖 *include
的行为。这在遗留的Rails代码中非常突出。more details from Yehuda Katz。关于
include
及其默认行为的更多详细信息,假设您已经运行了以下代码@@foo
或@@bar
super
将在检查Klazz的真实的超类的foo方法之前检查Mod#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 rubydoc798qvoo82#
你说的是对的。然而,还有更多的事情。
如果你有一个类
Klazz
和一个模块Mod
,那么在Klazz
中包含Mod
就可以让Klazz
的示例访问Mod
的方法。或者,您可以使用Mod
扩展Klazz
,让 classKlazz
访问Mod
的方法。但是您也可以使用o.extend Mod
扩展任意对象。在这种情况下,单个对象获得Mod
的方法,即使所有其他与o
具有相同类的对象都没有。kb5ga3dv3#
是的,没错。
在幕后,include实际上是append_features的别名,它(来自文档):
Ruby的默认实现是将这个模块的常量、方法和模块变量添加到aModule中,如果这个模块还没有被添加到aModule或它的祖先中的话。
u4vypkhs4#
当你将一个模块**
include
导入到一个类中时,模块的方法被导入为示例方法**。但是,当你将一个模块**
extend
导入到一个类中时,模块方法将作为类方法**导入。例如,如果我们有一个模块
Module_test
定义如下:现在,对于**
include
**模块。如果我们定义类A
如下:输出将为:
M - in module
。如果我们将
include Module_test
替换为extend Module_test
并再次运行代码,我们会收到以下错误:undefined method 'func' for #<A:instance_num> (NoMethodError)
。将方法调用
a.func
更改为A.func
,输出将更改为:M - in module
。从上面的代码执行中可以清楚地看到,当我们**
include
一个模块时,它的方法变成了示例方法**,当我们**extend
一个模块时,它的方法变成了类方法**。wz1wpwve5#
所有其他的答案都很好,包括挖掘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类中 include 模块ReusableModule,则方法、常量、类、子模块和其他声明将被引用。
如果你用模块ReusableModule * 扩展 * class ClassThatExtends,那么方法和常量会被 * 复制 *。显然,如果不小心,动态复制定义会浪费大量内存。
如果您使用ActiveSupport::Concern,. included()功能允许您直接重写including类。Concern中的模块ClassMethods被 * 扩展 *(复制)到包含类中。
iqih9akk6#
我也想解释一下它的运作机制。如果我说得不对,请纠正。
当我们使用
include
时,我们添加了一个从类到包含一些方法的模块的链接。对象没有方法,只有类和模块有。因此,当
a
接收到消息some_method
时,它开始在a
的特征类中搜索方法some_method
,然后在A
类中搜索,然后在链接到A
类的模块中搜索(如果有一些模块的话,按相反的顺序,最后包含的模块获胜)。当我们使用
extend
时,我们正在向对象的特征类中的模块添加链接。因此,如果我们使用A.new.extend(MyMod),我们将模块链接到A的示例特征类或a'
类。如果我们使用A.extend(MyMod),我们将链接添加到A(对象的,类也是对象)特征类A'
。所以
a
的方法查找路径如下:a => a' =>将模块链接到a' class => A。还有一个prepend方法可以改变查找路径:
a => a' => prepended modules to A => A => included modules to A
对不起我的英语不好。
ttvkxqim7#
我遇到了一个非常有用的article,它比较了
include
,extend
和prepend
在类中使用的方法:include
将模块方法作为示例方法添加到类中,而extend
将模块方法作为类方法添加。必须相应地定义要包含或扩展的模块mtb9vblg8#
include提供了一个类,它可以访问模块的方法作为示例
extend提供了一个类,可以访问模块的方法作为类方法