Ruby 中的包含和扩展有什么区别?
我刚刚了解 Ruby 元编程。 mixin/模块总是让我感到困惑。
- include:混入指定模块方法作为目标类中的实例方法
- extend:混入指定模块方法作为类方法目标类别中的strong>
那么主要区别只是这个还是潜伏着一条更大的龙? 例如
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!"
Just getting my head around Ruby metaprogramming. The mixin/modules always manage to confuse me.
- include: mixes in specified module methods as instance methods in the target class
- extend: mixes in specified module methods as class methods in the target class
So is the major difference just this or is a bigger dragon lurking?
e.g.
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!"
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
extend - 将指定模块的方法和常量添加到目标的元类(即单例类)
例如,
Klazz.extend(Mod)
,现在 Klazz 具有 Mod 的方法(作为类方法)obj.extend(Mod)
,现在 obj 具有 Mod 的方法(作为实例方法),但 obj.class 的其他实例没有添加这些方法。extend
是一个公共方法include - 默认情况下,它混合指定模块的方法作为目标模块/类中的实例方法。
例如,
class Klazz; 包括模组; end;
,现在 Klazz 的所有实例都可以访问 Mod 的方法(作为实例方法)include
是一个私有方法,因为它旨在从容器类/模块内调用。但是,模块经常通过对
included
方法进行猴子修补来覆盖include
的行为。 这在遗留 Rails 代码中非常突出。 来自 Yehuda Katz 的更多详细信息。关于
include
的更多详细信息,及其默认行为,假设您已运行以下代码@@foo
或@@bar
super
将在检查 Klazz 的真正超类的 foo 方法之前检查 Mod#foo。有关详细信息,请参阅 RubySpec。)。当然,Ruby 核心文档始终是获取这些内容的最佳位置事物。 RubySpec 项目也是一个很棒的资源,因为他们精确地记录了功能。
#include
RubySpec < a href="http://www.ruby-doc.org/core-1.9.3/Module.html#method-i-include" rel="noreferrer">rubydoc#included RubySpec rubydoc
#extend
RubySpec rubydoc#extended
RubySpec rubydoc#extend_object
RubySpec rubydoc#append_features
RubySpec rubydocextend - adds the specified module's methods and constants to the target's metaclass (i.e. the singleton class)
e.g.
Klazz.extend(Mod)
, now Klazz has Mod's methods (as class methods)obj.extend(Mod)
, now obj has Mod's methods (as instance methods), but no other instance of ofobj.class
has those methods added.extend
is a public methodinclude - By default, it mixes in the specified module's methods as instance methods in the target module/class.
e.g.
class Klazz; include Mod; end;
, now all instances of Klazz have access to Mod's methods (as instance methods)include
is a private method, because it's intended to be called from within the container class/module.However, modules very often override
include
's behavior by monkey-patching theincluded
method. This is very prominent in legacy Rails code. more details from Yehuda Katz.Further details about
include
, with its default behavior, assuming you've run the following code@@foo
or@@bar
super
in Klazz#foo will check for Mod#foo before checking to Klazz's real superclass's foo method. See the RubySpec for details.).Of course, the ruby core documentation is always the best place to go for these things. The RubySpec project was also a fantastic resource, because they documented the functionality precisely.
#include
RubySpec rubydoc#included
RubySpec rubydoc#extend
RubySpec rubydoc#extended
RubySpec rubydoc#extend_object
RubySpec rubydoc#append_features
RubySpec rubydoc你说的是正确的。 然而,事情还不止于此。
如果您有一个类
Klazz
和模块Mod
,在Klazz
中包含Mod
会给出Klazz< 的实例/code> 访问
Mod
的方法。 或者,您可以使用Mod
扩展Klazz
,为 类Klazz
提供对Mod
的访问权限。方法。 但您也可以使用o.extend Mod
扩展任意对象。 在这种情况下,单个对象可以获得Mod
的方法,即使与o
具有相同类的所有其他对象都没有。What you have said is correct. However, there is more to it than that.
If you have a class
Klazz
and moduleMod
, includingMod
inKlazz
gives instances ofKlazz
access toMod
's methods. Or you can extendKlazz
withMod
giving the classKlazz
access toMod
's methods. But you can also extend an arbitrary object witho.extend Mod
. In this case the individual object getsMod
's methods even though all other objects with the same class aso
do not.这是正确的。
在幕后, include 实际上是 append_features 的别名,它(来自文档):
That's correct.
Behind the scenes, include is actually an alias for append_features, which (from the docs):
当您将模块
包含
到类中时,模块方法将作为实例方法导入。但是,当您
将模块扩展
为类时,模块方法将作为类方法导入。例如,如果我们有一个模块
Module_test
定义如下:现在,对于
include
模块。 如果我们按如下方式定义类A
:输出将为:
M - in module
。如果我们将
include Module_test
行替换为extend Module_test
并再次运行代码,我们会收到以下错误:undefined method 'func' for #(无方法错误)
。将方法调用
a.func
更改为A.func
,输出更改为:M - in module
。从上面的代码执行中可以清楚地看出,当我们
包含
一个模块时,它的方法会变成实例方法,而当我们扩展
一个模块,它的方法成为类方法。When you
include
a module into a class, the module methods are imported as instance methods.However, when you
extend
a module into a class, the module methods are imported as class methods.For example, if we have a module
Module_test
defined as follows:Now, for
include
module. If we define the classA
as follows:The output will be:
M - in module
.If we replace the line
include Module_test
withextend Module_test
and run the code again, we receive the following error:undefined method 'func' for #<A:instance_num> (NoMethodError)
.Changing the method call
a.func
toA.func
, the output changes to:M - in module
.From the above code execution, it is clear that when we
include
a module, its methods become instance methods and when weextend
a module, its methods become class methods.所有其他答案都很好,包括深入研究 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
至于用例:
如果您包括 类 ClassThatInincludes 中的模块 ReusableModule、方法、常量、类、子模块和其他声明都被引用。
如果您使用模块 ReusableModule 来扩展类 ClassThatExtends,那么方法和常量就会被复制。 显然,如果您不小心,动态复制定义可能会浪费大量内存。
如果您使用 ActiveSupport::Concern,.included() 功能可让您直接重写包含类。 关注点内的模块 ClassMethods 被扩展(复制)到包含的类中。
All the other answers are good, including the tip to dig through 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
As for use cases:
If you include module ReusableModule in class ClassThatIncludes, the methods, constants, classes, submodules, and other declarations gets referenced.
If you extend class ClassThatExtends with module ReusableModule, then the methods and constants gets copied. Obviously, if you are not careful, you can waste a lot of memory by dynamically duplicating definitions.
If you use ActiveSupport::Concern, the .included() functionality lets you rewrite the including class directly. module ClassMethods inside a Concern gets extended (copied) into the including class.
我还想解释一下其运作机制。 如果我说得不对请指正。
当我们使用
include
时,我们添加了从类到包含一些方法的模块的链接。对象没有方法,只有类和模块有。
因此,当
a
收到消息some_method
时,它开始在a
的特征类中搜索方法some_method
,然后在>A
类,然后链接到A
类模块(如果有)(按相反顺序,最后包含的获胜)。当我们使用
extend
时,我们正在添加到对象的特征类中的模块的链接。因此,如果我们使用 A.new.extend(MyMod),我们会将模块的链接添加到 A 的实例特征类或
a'
类。如果我们使用 A.extend(MyMod),我们将添加到 A(对象,类也是对象)eigenclass
A'
的链接。所以
a
的方法查找路径如下:一个=> 一个' => 将模块链接到“类=>” A.
还有一个改变查找路径的前置方法:
a => 一个' => A => 前面的模块 A=> 包含模块,抱歉
我的英语不好。
I would also like to explain the mechanism as it works. If I am not right please correct.
When we use
include
we are adding a linkage from our class to a module which contains some methods.Objects don't have methods, only clases and modules do.
So when
a
receives mesagesome_method
it begin search methodsome_method
ina
's eigen class, then inA
class and then in linked toA
class modules if there are some (in reverse order, last included wins).When we use
extend
we are adding linkage to a module in object's eigen class.So if we use A.new.extend(MyMod) we are adding linkage to our module to A's instance eigen class or
a'
class.And if we use A.extend(MyMod) we are adding linkage to A(object's, classes are also objects) eigenclass
A'
.so method lookup path for
a
is as follows:a => a' => linked modules to a' class => A.
also there is a prepend method which changes lookup path:
a => a' => prepended modulesto A => A => included module to A
sorry for my bad english.
我发现了一篇非常有用的文章比较类内使用的
include
、extend
和prepend
方法:include
添加模块方法作为类的实例方法,而extend
将模块方法添加为类方法。 必须相应地定义所包含或扩展的模块I came across a very useful article that compares
include
,extend
andprepend
methods used inside a class:include
adds module methods as instance methods to the class, whereasextend
adds module methods as class methods. The module being included or extended must be defined accordinglyinclude 为类提供对模块方法的访问作为实例
extend 为类提供对模块方法的访问作为类方法
include provides a class with access to a module's methods as an instance
extend provides a class with access to a module's methods as class methods