我只是在了解Ruby元编程。mixin/modules总是把我弄糊涂。

Include:在目标类中混合指定的模块方法作为实例方法 扩展:在目标类中混合指定的模块方法作为类方法

所以主要的区别是这一点,还是有更大的龙潜伏着? 如。

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

当前回答

我还想解释一下它的工作机制。如果我说得不对,请指正。

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

class A
include MyMOd
end

a = A.new
a.some_method

对象没有方法,只有类和模块有。 因此,当一个接收到消息some_method时,它开始在a的特征类中搜索方法some_method,然后在a类中搜索方法some_method,然后在链接到a类的模块中搜索方法some_method(如果有的话)(倒序,最后包含的胜出)。

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

a的方法查找路径如下: a' => a' =>链接模块到a' class =>

还有一个prepend方法可以改变查找路径:

a => a' =>前置模块到a => a =>包含模块到a

对不起,我的英语不好。

其他回答

扩展——将指定模块的方法和常量添加到目标的元类(即单例类) 如。

如果你调用Klazz.extend(Mod),现在Klazz有Mod的方法(作为类方法) 如果你调用obj.extend(Mod),现在obj有Mod的方法(作为实例方法),但obj.class的其他实例没有添加这些方法。 Extend是一个公共方法

include——默认情况下,它将指定模块的方法作为目标模块/类的实例方法混合在一起。 如。

如果你称班级为Klazz;包括国防部;,现在Klazz的所有实例都可以访问Mod的方法(作为实例方法) Include是一个私有方法,因为它打算从容器类/模块内部调用。

然而,模块经常通过对所包含的方法进行猴子补丁来覆盖include的行为。这在遗留Rails代码中非常突出。Yehuda Katz报道。

关于include的更多详细信息及其默认行为,假设您已经运行了以下代码

class Klazz
  include Mod
end

If Mod is already included in Klazz, or one of its ancestors, the include statement has no effect It also includes Mod's constants in Klazz, as long as they don't clash It gives Klazz access to Mod's module variables, e.g. @@foo or @@bar raises ArgumentError if there are cyclic includes Attaches the module as the caller's immediate ancestor (i.e. It adds Mod to Klazz.ancestors, but Mod is not added to the chain of Klazz.superclass.superclass.superclass. So, calling super in Klazz#foo will check for Mod#foo before checking to Klazz's real superclass's foo method. See the RubySpec for details.).

当然,ruby核心文档始终是实现这些功能的最佳途径。RubySpec项目也是一个极好的资源,因为他们精确地记录了功能。

包括RubySpec rubydoc 包括RubySpec rubydoc 扩展RubySpec rubydoc 扩展的RubySpec rubydoc #extend_object RubySpec rubydoc # append_featuresrubyspec rubydoc

你说得对。然而,事情远不止如此。

如果你有一个类Klazz和模块Mod,在Klazz中包含Mod可以让Klazz的实例访问Mod的方法。或者你可以用Mod扩展Klazz,让类Klazz可以访问Mod的方法。但是你也可以用o.extend Mod扩展任意对象。在这种情况下,单个对象获得Mod的方法,而与o具有相同类的所有其他对象都没有。

所有其他的答案都很好,包括挖掘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()函数可以让你直接重写include类。关注点中的模块ClassMethods被扩展(复制)到包含类中。

我还想解释一下它的工作机制。如果我说得不对,请指正。

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

class A
include MyMOd
end

a = A.new
a.some_method

对象没有方法,只有类和模块有。 因此,当一个接收到消息some_method时,它开始在a的特征类中搜索方法some_method,然后在a类中搜索方法some_method,然后在链接到a类的模块中搜索方法some_method(如果有的话)(倒序,最后包含的胜出)。

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

a的方法查找路径如下: a' => a' =>链接模块到a' class =>

还有一个prepend方法可以改变查找路径:

a => a' =>前置模块到a => a =>包含模块到a

对不起,我的英语不好。

这是正确的。

在幕后,include实际上是append_features的别名,它(来自文档):

Ruby的默认实现是to 添加常量、方法和模块 将该模块的变量转换为aModule if 此模块尚未添加 阿莫杜勒或它的祖先之一