如何在Ruby中使用模块覆盖静态类方法? [英] How to override static class method using module in 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屋!