在模块中使用extend self

时间:2015-04-21 18:35:05

标签: ruby module include extend self

在因question duplication而投票结束之前,我想说我的问题非常简单(在上述问题中未提及)。

有两个模块,一个使用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

有一个类,我includeextend这些模块:

class Test
  include A
  extend A
  include B
  extend B
end

当我们include模块时,它的方法变成了类'实例方法,当extend ing - 类方法时。

问题: 如果模块中的方法定义为 module 方法或 mixin 方法,那么对于类来说并不重要,对吗?我的意思是,当include d - 每个方法(模块方法或mixin方法)变为实例方法,以及extend时 - 变为类方法

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

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

修改

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

3 个答案:

答案 0 :(得分:2)

无论您使用的是extend还是include,您始终都会复制实例方法。不同之处在于这些实例方法的存在。

当您调用Class#include时,您正在将模块中的所有实例方法“复制”为类中的实例方法。它类似于继承的工作方式,如果你调用Class#ancestors,你会在那里看到模块。

当您调用Object#extend时,您正在将模块的所有实例方法复制到对象的单例类。这是仅为在运行时创建的此对象实例保留的类。这就是你获得“类方法”的方法(例如MyClass.hello_world);通过将它们添加到类的单例中。您还可以执行扩展特定对象实例(例如s = String.new; s.extend(SomeModule); s.hello_world

之类的操作

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

当尝试添加“类”和实例方法时,您将看到的一个常见模式是使用included回调来使用ClassMethods模块扩展基类:

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

  module ClassMethods
    def hello_world
    end
  end
end

ActiveSupport::Concerns也抽象了这种模式,允许你在一次调用中添加实例和“类”方法。

我个人更喜欢让模块只使用实例方法并使用单例方法(例如def self.my_method)来设置范围方法(类似于如何使用私有方法)。这允许消费者使用他们想要的extendinclude并使其按预期工作。

我不确定这是否能回答你的问题,但是有一些信息可供你使用

答案 1 :(得分:2)

让我们分步看一下。

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

includingextend ing 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

答案 2 :(得分:1)

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

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

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

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

module SomeModule
  def hi
    'hi'
  end
end

class SomeClass
  extend SomeModule
end

class SomeClass
  class << self
    include SomeModule
  end
end

考虑到这一点,在你所说的模块中使用extend self:采用我定义的所有mixin方法并使用它们扩展模块的单例类。这种魔力是红宝石本质的结果:重新打开任何定义的能力。以下是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