ruby - 如何在 Ruby 中使用模块覆盖静态类方法?

标签 ruby

module Imodule
  ???
end

class Some
  include Imodule

  def self.imethod
    puts "original"
  end
end

Some.imethod
# => "overrided"

如何创建一个覆盖静态方法的模块?

这是一道深入理解ruby特性的面试题。不要提出问题的另一种表述:)

最佳答案

好的,这是一个工作代码。请注意,您甚至不必触摸目标类! :)

class Klass
  def self.say
    puts 'class'
  end
end

module FooModule
  def self.included base
    base.instance_eval do
      def say
        puts "module"
      end
    end
  end
end


Klass.send(:include, FooModule)

Klass.say

解释

现在混合类方法的经典方式是这样(当然,它不能解决问题)。

module FooModule
  def self.included base
    base.extend ClassMethods
  end

  module ClassMethods
    def bar
      puts "module"
    end
  end
end

class Klass
  include FooModule

  def self.bar
    puts 'class'
  end
end


Klass.bar #=> class

当模块被包含或扩展到一个类中时,它的方法在继承链中位于该类方法的正上方。这意味着如果我们在该类方法中调用 super,它将打印“module”。但我们不想触及原始类定义,我们想从外部更改它。

那么,我们可以做点什么吗?

对我们有好处,ruby 有一个概念 "open classes" .这意味着我们几乎可以更改应用程序中的所有内容,甚至是一些第 3 方库。每个类都可以“打开”,可以向其中添加新方法,也可以重新定义旧方法。让我们看看它是如何工作的。

class Klass
  def self.bar
    puts 'class'
  end
end

class Klass
  def self.bar
    puts 'class 2'
  end
end

Klass.bar #=> class 2

第二类定义不会覆盖前一个,它会打开并更改它。在这种情况下,恰好定义了一个同名的方法。这导致旧方法被新方法覆盖。这适用于任何类,甚至是基础库类。

puts [1, 2, 3].to_s #=> [1, 2, 3]

class Array
  def to_s
    "an array: #{join ', '}"
  end
end

puts [1, 2, 3].to_s #=> an array: 1, 2, 3

或者同样的代码可以重写为

puts [1, 2, 3].to_s #=> [1, 2, 3]

Array.class_eval do
  def to_s
    "an array: #{join ', '}"
  end
end

puts [1, 2, 3].to_s #=> an array: 1, 2, 3

应用知识

让我们从更简单的事情开始,比如覆盖实例方法。

class Klass
  def say
    puts 'class'
  end
end

module FooModule
  def self.included base
    base.class_eval do
      def say
        puts "module"
      end
    end
  end
end


Klass.send(:include, FooModule)

Klass.new.say #=> module

模块有一个特殊的回调函数,每次在类中包含一个模块时都会调用该回调函数。我们可以使用它来调用该类的 class_eval 并重新定义一个方法。

以类似的方式替换类方法。

class Klass
  def self.say
    puts 'class'
  end
end

module FooModule
  def self.included base
    base.instance_eval do
      def say
        puts "module"
      end
    end
  end
end


Klass.send(:include, FooModule)

Klass.say #=> module

这里唯一的区别是我们调用 instance_eval 而不是 class_eval。这可能是一个非常困惑的部分。简而言之,class_eval 创建实例方法,instance_eval 创建类方法。

这取 self 的 blog post .

关于ruby - 如何在 Ruby 中使用模块覆盖静态类方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9128515/

相关文章:

ruby - 在 Google Drive API 中收到错误 403 - 超出了未经身份验证使用的每日限制。继续使用需要注册

ruby - 如何从 Watir-Webdriver 更改 Opera 的代理设置?

ruby-on-rails - 使用 .include 检查数组中的多个项目? -- ruby 初学者

Ruby Open-URI - 指定内容类型?

ruby - 难以解决基本的 RubyMonk 练习

ruby-on-rails - acts_as_taggable_on Gem 不适用于 Rails 5

ruby - 如果在 1 行中返回的值为 0,我如何优雅地返回 1.0?

ruby - 在 Rails 3 应用程序中调用 .ajax 方法时出现 401 Unauthorized

ruby - 如何使用 Ruby 选择唯一的 XML 节点?

ruby-on-rails - 在模型中使用辅助方法