在模块中使用 extend self

Using extend self in module

由于 question duplication 投票结束之前,我想说我的问题真的很简单(没有在上述问题中提出)。

有两个模块,一个定义了模块方法 using extend self,另一个定义了mixin方法.

module A
  extend self
  def module_a_meth
    "Called module_a_meth"
  end
end

module B
  def module_b_meth
    "Called module_b_meth"
 end
end

有一个class,其中我includeextend这些模块:

class Test
  include A
  extend A
  include B
  extend B
end

当我们 includeing 模块时,它的方法变成 class' 实例方法,当 extending - class 方法。

问题: class,如果模块中的方法定义为 module 方法或 mixin 方法,对吧?我的意思是,当 included - 每个方法(模块方法或混合方法)变成 实例方法 ,当 extended - 要么变成 class 方法.

如果我错了 - 区别在哪里?

obj = Test.new

puts obj.module_a_meth
puts obj.module_b_meth
puts Test.module_a_meth
puts Test.module_b_meth

#=> Called module_a_meth
#=> Called module_b_meth
#=> Called module_a_meth
#=> Called module_b_meth

编辑

请以开始您的回答,因为我的问题暗示了这种类型的回答:)。

无论您使用的是 extend 还是 include,您总是在复制实例方法。不同之处在于这些实例方法所在的位置。

当您调用 Class#include 时,您 "copying" 模块中的所有实例方法都是 class 中的实例方法。它类似于继承的工作方式,如果你调用 Class#ancestors 你会在那里看到模块。

当您调用 Object#extend 时,您正在将模块的所有实例方法复制到对象的单例 class。这是一个 class 只为这个在运行时创建的对象实例保留的。这就是您获得 "class methods" 的方式(例如 MyClass.hello_world);通过将它们添加到 class 的单例中。您还可以做一些事情,比如扩展一个特定的对象实例(例如 s = String.new; s.extend(SomeModule); s.hello_world

还有其他一些差异。上下文绑定根据您使用的是 extend 还是 include 而不同。 extend 不会导致模块出现在祖先链中,而 include 会。

当尝试添加 "class" 和实例方法时,您会看到一种常见的模式是使用 included 回调来扩展基础 class一个 ClassMethods 模块:

module MyModule
  def self.included(base)
    base.extend ClassMethods
  end

  module ClassMethods
    def hello_world
    end
  end
end

ActiveSupport::Concerns 还抽象了此模式,允许您在一次调用中添加实例和 "class" 方法。

我个人更喜欢让模块只与实例方法一起工作,并使用单例方法(例如 def self.my_method)来使用作用域方法(有点像您使用私有方法的方式)。这允许消费者使用他们想要的 extendinclude 并使其按预期工作。

我不确定这是否能回答您的问题,但有一些信息适合您

让我们分步看一下。

module A
  puts "self = #{self}"
  extend self
  def module_a_meth
    "Called module_a_meth"
  end
end

class Test
end

Test.include A
  #-> self = Test

Test.instance_methods.include?(:module_a_meth)
  #=> true
Test.methods.include?(:module_a_meth)
  #=> false - no class method

所以 include 包含 :module_a_meth 作为实例方法。由于 selfTest,行:

 extend self

相当于:

 extend Test

这当然没有引用该模块。现在我们 extend 得到了预期的结果:

Test.extend A
  #=> true 

Test.methods.include?(:module_a_meth)
  #=> true

includingextending B 是正常的:

module B
  def module_b_meth
    "Called module_b_meth"
 end
end

Test.include B
Test.instance_methods.include?(:module_b_meth)
  #=> true
Test.extend B
Test.methods.include?(:module_b_meth)
  #=> true

首先,关于实际问题: :).

Class(或任何其他对象)关心如何在您包含的模块中定义方法。基本上,您描述的模块中的方法被定义为 mixin 方法。 extend self 没有将方法重新定义为模块方法,但基本上将它们复制到两个上下文中。

extend 是如何工作的问题,这只是一个棘手的案例。

首先,将 extend 视为对象的单例 class 上下文中的 include。这两个定义是相等的:

module SomeModule
  def hi
    'hi'
  end
end

class SomeClass
  extend SomeModule
end

class SomeClass
  class << self
    include SomeModule
  end
end

鉴于此,通过在模块中使用 extend self 你是在说:采用我定义的所有混合方法并使用它们扩展模块的单例 class。这种魔法是 ruby 的本性的结果:重新打开任何定义的能力。下面是 extend self 的详细版本:

   module Module1
      def hi
        'hi'
      end
    end

    module Module1
      extend Module1 # which is self

      #### now "hi" is both here:
      # def hi; end
      #### and here:
      # class << self; def hi; end
    end

    Module1.hi #  => 'hi'
    class SomeClass; include Module1; end;
    SomeClass.new.hi # => 'hi'

__ 编辑 __

快速证明对象关心模块中的方法是如何定义的:

module SomeModule
  def self.hi
    'hi'
  end
end

object = 'some string'
class << object
  include SomeModule
end
object.hi # => NoMethodError: undefined method