我只是在了解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!"

当前回答

当您将模块包含到类中时,模块方法将作为实例方法导入。

但是,当您将模块扩展为类时,模块方法将作为类方法导入。

例如,如果我们有一个模块Module_test,定义如下:

module Module_test
  def func
    puts "M - in module"
  end
end

现在,对于include模块。如果我们这样定义A类:

class A
  include Module_test
end

a = A.new
a.func

输出将是:M - in模块。

如果我们将include Module_test替换为extend Module_test并再次运行代码,我们会收到以下错误:undefined method 'func' for #<A:instance_num> (NoMethodError)。

将方法调用a.func更改为a.func,输出更改为:M - in模块。

从上面的代码执行可以清楚地看出,当我们包含一个模块时,它的方法就变成了实例方法,而当我们扩展一个模块时,它的方法就变成了类方法。

其他回答

这是正确的。

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

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

当您将模块包含到类中时,模块方法将作为实例方法导入。

但是,当您将模块扩展为类时,模块方法将作为类方法导入。

例如,如果我们有一个模块Module_test,定义如下:

module Module_test
  def func
    puts "M - in module"
  end
end

现在,对于include模块。如果我们这样定义A类:

class A
  include Module_test
end

a = A.new
a.func

输出将是:M - in模块。

如果我们将include Module_test替换为extend Module_test并再次运行代码,我们会收到以下错误:undefined method 'func' for #<A:instance_num> (NoMethodError)。

将方法调用a.func更改为a.func,输出更改为:M - in模块。

从上面的代码执行可以清楚地看出,当我们包含一个模块时,它的方法就变成了实例方法,而当我们扩展一个模块时,它的方法就变成了类方法。

我看到了一篇非常有用的文章,比较了类中使用的include、extend和prepend方法:

Include将模块方法作为实例方法添加到类中,而extend将模块方法作为类方法添加。要包含或扩展的模块必须相应定义

所有其他的答案都很好,包括挖掘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

对不起,我的英语不好。