我只是在了解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模块。
从上面的代码执行可以清楚地看出,当我们包含一个模块时,它的方法就变成了实例方法,而当我们扩展一个模块时,它的方法就变成了类方法。
当您将模块包含到类中时,模块方法将作为实例方法导入。
但是,当您将模块扩展为类时,模块方法将作为类方法导入。
例如,如果我们有一个模块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模块。
从上面的代码执行可以清楚地看出,当我们包含一个模块时,它的方法就变成了实例方法,而当我们扩展一个模块时,它的方法就变成了类方法。
所有其他的答案都很好,包括挖掘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
对不起,我的英语不好。