钩子方法:当执行或者调用某个方法的时候,和这个方法相关联的方法会被执行
钩子方法在被定义的时候必须被定义为类方法。
def self.hooked
puts "hello world"
end
钩子方法的两种形式:模块中和类中
#模块中需要被定义为模块方法
module Demo
def inherited(child)
puts "hello world"
end
end
class Father
extend Demo
end
class Son < Father
end
#类中定义为类方法
class Father
def self.inherited(child)
puts "hello world"
end
end
class Son < Father
end
下面介绍元编程书中讲述的几种hook方法,分别是分别介绍
#included, extended, prepended, inherited;
#method_added, method_removed, method_undefined;
#singleton_method_added, singleton_method_removed, singleton_method_undefined;
#included形式
module M
def self.included(othermod)
puts "#{self} was mixed into #{othermod}"
end
end
class C
include M #=> M was mixed into C
end
#extended形式
module A
def self.extended(mod)
puts "#{self} extended in #{mod}"
end
def demo
return "this is the demo"
end
end
module Enumerable
extend A
end
Enumerable.demo #this is the demo
#prepended形式
module Demo
def self.prepended(mod)
puts "#{self} and #{mod}"
end
def one
puts "this is the one method in demo"
end
end
class Prepend
prepend Demo #Demo and Prepend
end
Prepend.new.one
#inherited
class Father
def self.inherited(child)
puts "#{self} and #{child}"
end
end
class Son < Father
end #Father and Son
#method_added, method_removed, method_undefined
class Simulate
def self.method_added(method)
puts "#{self} and #{method}"
end
def self.method_removed(method)
puts "#{self} and #{method}"
end
def self.method_undefined(method)
puts "#{self} and #{method}"
end
def one; end #trigger method_added
def two; end #trigger method_added
remove_method :one #trigger method_removed
undef_method :two #trigger method_undefined
end
#比较undef_method和remove_method方法
undef_method: 删除所有的方法,包括继承来的方法
remove_method: 删除自己的方法,但是保留继承来的方法
#singleton_method_added, singleton_method_removed, singleton_method_undefined
class Simulate
def self.singleton_method_added(method)
puts "#{self} and #{method}"
end
def self.singleton_method_removed(method)
puts "#{self} and #{method}"
end
def self.singleton_method_undefined(method)
puts "#{self} and #{method}"
end
def self.hello; end
class << self
remove_method(:hello)
end
def self.hello; end
class << self
undef_method(:hello)
end
end
#result
Simulate and singleton_method_added
Simulate and singleton_method_removed
Simulate and singleton_method_undefined
Simulate and hello
Simulate and hello
Simulate and hello
Simulate and hello
注意点:
1.singleton_method_added会回调自己
2.必须在单例类中执行remove_method和undef_method方法
钩子方法include方法和extend方法的应用
#类扩展:在该类的eigenclass中include模块,使得模块中的实例方法成为类的类方法。
module M
def demo_method
puts "this is the demo method in module"
end
end
class DemoClass
extend M
end
DemoClass.demo_method #=> this is the demo method in module
#类扩展混入:钩子方法和类扩展结合。
- 定义一个模块,比如MyMixin
- 在MyMixin中定义一个内部模块,这些方法最终会成为类方法。
- 覆写MyMixin#included()方法来用ClassMethods扩展包含者(使用extend()方法)
module MyMixin
def self.included(base)
base.extend(ClassMethods)
#Object#extend()只是接受者eigenclass中包含模块的快捷方式。
end
module ClassMethods
def demo_method
puts "this is the method in the module"
end
end
end
class DemoClass
include MyMixin
end
DemoClass.demo_method #=> this is the method in the module
#如果不需要为包含着提供实例方法,可以没有必要提供内部模块,直接在MyMixin中定义方法即可,这个方法会成为被包含着的类方法:
module MyMixin
def self.included(base)
base.extend(self)
end
def demo_method
puts "this is the method in the module"
end
end
class DemoClass
include MyMixin
end
DemoClass.demo_method #=> this is the method in the module
注意的问题
1.钩子方法都需要被定义为单例方法
2.钩子方法的参数是方法名,而不是类名,通过self获取类的信息
3.不要调用钩子方法相关的三个方法:
extend_object, append_features, prepend_features
比如在include方法执行的时候,在included钩子方法被执行之前,模块的append_features方法会被调用并执行具体的include操作。但是推荐的用法是定义included钩子方法。