在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
Mod
给Klazz
访问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_method
在a
特征类,然后在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中
对不起,我的英语不好。