位置:首页 > Ruby > Ruby钩子方法实战:常见运用场景与实例解析

Ruby钩子方法实战:常见运用场景与实例解析

时间:2026-08-18  |  作者:深海捕梦者  |  阅读:0

在 Ruby 里,钩子方法(Hook Methods)就像一把手术刀,允许我们在类或模块的生命周期关键时刻精准介入。

用好它,代码的灵活性和可维护性都能上一个台阶。下面把这些生命周期相关的钩子方法梳理一下。

Ruby中钩子方法的运用实例解析

类与模块相关的钩子

  • Class#inherited —— 当子类被继承时触发
  • Module#include —— 模块被混入时调用
  • Module#prepended —— 模块被前置混入时触发
  • Module#extend_object —— 对象被扩展时调用
  • Module#method_added —— 实例方法被添加时触发
  • Module#method_removed —— 实例方法被移除时触发
  • Module#method_undefined —— 实例方法被置为 undefined 时触发

单件类相关的钩子

  • BasicObject#singleton_method_added —— 单件方法被添加时触发
  • BasicObject#singleton_method_removed —— 单件方法被移除时触发
  • BasicObject#singleton_method_undefined —— 单件方法被置为 undefined 时触发

示例代码

module M1
  def self.included(othermod)
    puts "M1 was included into #{othermod}"
  end
end

module M2
  def self.prepended(othermod)
    puts "M2 was prepended to #{othermod}"
  end
end

class C
  include M1
  include M2
end

# 输出
M1 was included into C
M2 was prepended to C

module M
  def self.method_added(method)
    puts "New method: M##{method}"
  end

  def my_method; end
end

# 输出
New method: M#my_method

除了上面列出的专用钩子,我们还可以通过重写父类方法来实现类似效果。

常见做法是先加入过滤逻辑,再用 super 调用原功能。环绕别名(Around Alias)也是一种常见的替代方案。

运用实例

任务描述:

写一个类似 attr_accessor 的类宏 attr_checked,用来对属性值做校验。使用方式如下:

class Person
  include CheckedAttributes

  attr_checked :age do |v|
    v >= 18
  end
end

me = Person.new
me.age = 39   # ok
me.age = 12   # 抛出异常

实施计划:

  • 用 eval 方法编写一个 add_checked_attribute 内核方法,为指定类添加经过简单校验的属性
  • 重构该方法,去掉 eval,改用其他手段实现
  • 添加代码块校验功能
  • 修改方法名为 attr_checked,并使其对所有类都可用
  • 通过引入模块的方式,只对引入该功能的类添加 attr_checked

Step 1

def add_checked_attribute(klass, attribute)
  eval "
    class #{klass}
      def #{attribute}=(value)
        raise 'Invalid attribute' unless value
        @#{attribute} = value
      end
      def #{attribute}()
        @#{attribute}
      end
    end
  "
end

add_checked_attribute(String, :my_attr)
t = "hello,kitty"

t.my_attr = 100
puts t.my_attr

t.my_attr = false
puts t.my_attr

这一步直接用 eval 打开类并定义 get/set 方法。

set 方法会判断值是否为空(nil 或 false),否则抛出异常。虽然粗暴,但胜在直观。

Step 2

def add_checked_attribute(klass, attribute)
  klass.class_eval do
    define_method "#{attribute}=" do |value|
      raise "Invalid attribute" unless value
      instance_variable_set("@#{attribute}", value)
    end

    define_method attribute do
      instance_variable_get "@#{attribute}"
    end
  end
end

这一步替换掉了 eval

改用 class_evaldefine_method。实例变量的读写也换成了 instance_variable_setinstance_variable_get

功能完全一样,但实现更安全、更 Ruby 化。

Step 3

def add_checked_attribute(klass, attribute, &validation)
  klass.class_eval do
    define_method "#{attribute}=" do |value|
      raise "Invalid attribute" unless validation.call(value)
      instance_variable_set("@#{attribute}", value)
    end

    define_method attribute do
      instance_variable_get "@#{attribute}"
    end
  end
end

add_checked_attribute(String, :my_attr){|v| v >= 180 }
t = "hello,kitty"

t.my_attr = 100     # Invalid attribute (RuntimeError)
puts t.my_attr

t.my_attr = 200
puts t.my_attr       # 200

这一步增加了一个代码块参数,让校验逻辑更灵活。

不再局限于简单的非空判断,可以自定义任何条件。

Step 4

class Class
  def attr_checked(attribute, &validation)
    define_method "#{attribute}=" do |value|
      raise "Invalid attribute" unless validation.call(value)
      instance_variable_set("@#{attribute}", value)
    end

    define_method attribute do
      instance_variable_get "@#{attribute}"
    end
  end
end

String.attr_checked(:my_attr){|v| v >= 180 }
t = "hello,kitty"

t.my_attr = 100     # Invalid attribute (RuntimeError)
puts t.my_attr

t.my_attr = 200
puts t.my_attr       # 200

这一步把方法直接放到 Class 中,所有类都能调用。

因为在类内部 self 就是当前类,所以省去了 class_eval 和额外参数,方法名也改为 attr_checked

Step 5

module CheckedAttributes
  def self.included(base)
    base.extend ClassMethods
  end
end

module ClassMethods
  def attr_checked(attribute, &validation)
    define_method "#{attribute}=" do |value|
      raise "Invalid attribute" unless validation.call(value)
      instance_variable_set("@#{attribute}", value)
    end

    define_method attribute do
      instance_variable_get "@#{attribute}"
    end
  end
end

class Person
  include CheckedAttributes

  attr_checked :age do |v|
    v >= 18
  end
end

最后一步,利用钩子方法 included,在模块被混入时自动扩展目标类,使其获得 attr_checked

这样只对显式 include CheckedAttributes 的类生效,精确控制权限。

总结

到此,我们实现了一个类似 attr_accessor 的类宏 attr_checked,可以对属性值进行自定义校验。

整个过程展示了钩子方法在实际设计中的力量。

  • evaldefine_method
  • 再到模块扩展
  • 实现方式步步进化,越来越优雅

免责声明:文中图文均来自网络,如有侵权请联系删除,心愿游戏发布此文仅为传递信息,不代表心愿游戏认同其观点或证实其描述。

相关文章

更多

精选合集

更多

大家都在玩

热门话题

大家都在看

更多