Ruby prepend不适用于类方法

时间:2018-05-28 11:12:36

标签: ruby

我正在尝试在ruby中使用prepend方法来覆盖类的方法,这是我的代码的工作原理:

module PrependedModule
  def self.klass_method
    puts 'PrependedModule klass_method'
  end

  def instance_method_a
    puts 'PrepenedModule instance method'
  end
end

class SomeClass
  prepend PrependedModule

  def self.klass_method
    puts 'SomeClass klass_method'
  end

  def instance_method_a
    puts 'SomeClass instance_method'
  end
end

SomeClass.klass_method
SomeClass.new.instance_method_a

#output:
#SomeClass klass_method
#PrependedModule instance method

#expected:
#PrependedModule klass_method
#PrependedModule instance method

上面显示了此脚本的输出,正如我们所看到的,当我调用{时,实例方法instance_method_a被模块PrependedModule覆盖,而不是类方法klass_method。在klass_method上{1}},它仍然执行它的原始方法,而不是SomeClass中定义的方法。

我对此感到困惑,并且在使用prepend时不知道类方法发生了什么。如果有人能为我解决这个问题,那将是一个很大的帮助。

2 个答案:

答案 0 :(得分:3)

Singleton类不能那样工作。您需要明确prepend SomeClass的本征类的方法:

module PrependedModule
  module ClassMethods
    def klass_method
      puts 'PrependedModule klass_method'
    end
  end

  def instance_method_a
    puts 'PrepenedModule instance method'
  end
end

class SomeClass
  # prepending to the class
  prepend PrependedModule
  class << self
    # prepending to the eigenclass
    prepend PrependedModule::ClassMethods
  end

  def self.klass_method
    puts 'SomeClass klass_method'
  end

  def instance_method_a
    puts 'SomeClass instance_method'
  end
end

答案 1 :(得分:1)

使用includeprepend,您只能访问模块的实例方法 1 。因此,您可能会询问是否有任何理由在模块上定义模块方法。答案是响亮的&#34;是的&#34;。您可能希望这样做有两个原因。

第一个与包含或添加模块无关。您只需要查看模块Math即可了解您可能希望这样做的原因。 Math上定义的所有方法都是模块方法。这些构成了有用功能的库。它们当然是方法,但由于它们都有Math作为接收器,因此它们的行为类似于非OOP语言中的函数。

第二个原因是您可能希望在要包含或由其他模块预先添加的模块上定义callback method(又名 hook 方法)。主要是Module#includedModule#prependedModule#extendedClass#inheritedBasicObject#method_missing。最后一个是实例方法;其他是模块方法。以下是Module#prepended的示例。

@mudasoba已经展示了如何将实例方法限制为Sub的子模块Mod,以便Mod::Sub可以预先(或包含)到类({1}}(或单元类。使用回调Module#prepended的常用模式。接下来。

module PrependedModule
  module ClassMethods
    def klass_method
      puts 'PrependedModule klass_method'
    end
  end

  def instance_method_a
    puts 'PrepenedModule instance method'
  end

  def self.prepended(mod)
    mod.singleton_class.prepend(ClassMethods)
  end
end

class SomeClass
  def self.klass_method
    puts 'SomeClass klass_method'
  end

  def instance_method_a
    puts 'SomeClass instance_method'
  end

  prepend PrependedModule
end

SomeClass.klass_method
  # PrependedModule klass_method
SomeClass.new.instance_method_a
  # PrepenedModule instance method

1我总是觉得奇怪的是,可以在模块(不是类)上定义实例方法,考虑到这些模块不能有实例。确实,这些方法在包含或添加模块的类中成为实例方法,但请记住,这些模块也可以包含在其他模块中(或不是类)。因此,人们可能期望这些方法具有除&#34;实例方法&#34;之外的某些名称。然而,找到一个合适的替代方案将是一个挑战,这可能是该命名法持续存在的一个原因。