如何在Ruby中使用模块覆盖静态类方法? [英] How to override static class method using module in Ruby?

查看:44
本文介绍了如何在Ruby中使用模块覆盖静态类方法?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

module Imodule
  ???
end

class Some
  include Imodule

  def self.imethod
    puts "original"
  end
end

Some.imethod
# => "overrided"

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

How to create a module which will override static method?

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

This is an interview question for deep understanding ruby features. Don't suggest another formulation of the problem :)

推荐答案

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

Ok, here's a working code. Note that you don't even have to touch target class! :)

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

说明

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

Explanation

Now classic way of mixing in class methods is this (and it doesn't solve the problem, of course).

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".但是我们不想触及原始的类定义,我们想从外部改变它.

When modules are included or extended into a class, its methods are placed right above this class' methods in inheritance chain. This means that if we were to call super in that class method, it would print "module". But we don't want to touch original class definition, we want to alter it from outside.

对我们来说很好,ruby有一个"公开课".这意味着我们几乎可以更改应用程序中的所有内容,甚至是某些 3rd 方库.每个类都可以打开",可以向其中添加新方法,或者可以重新定义旧方法.让我们看看它是如何工作的.

Good for us, ruby has a concept of "open classes". This means that we can change virtually everything in the app, even some 3rd-party libraries. Every class can "opened" and new methods can be added to it, or old methods can be redefined. Let's look how it works.

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

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

Klass.bar #=> class 2

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

The second class definition does not overwrite previous one, it opens and alters it. In this case, it happened to define a method with the same name. This resulted in old method being overwritten by the new one. This works with any classes, even base library classes.

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

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

Or the same code can be rewritten as

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

应用知识

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

Applying the knowledge

Let's start with simpler things, like overriding an instance method.

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 并重新定义一个方法.

Modules have a special callback that gets called every time a module is included in a class. We can use that to call class_eval on that class and redefine a method.

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

Replacing a class method is done in a similar way.

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 创建类方法.

The only difference here is that we call instance_eval instead of class_eval. This can be a very confusing part. In short, class_eval creates instance methods and instance_eval creates class methods.

这摘自我的博客文章.

这篇关于如何在Ruby中使用模块覆盖静态类方法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆