Ruby prepend 方法
最后修改日期:2025 年 4 月 27 日
本教程解释了如何使用 Ruby 的 prepend
方法。这个强大的模块包含方法会改变类中的方法查找顺序。
prepend
方法将一个模块插入到类的祖先链之前。这意味着模块的方法会覆盖同名的类方法。它与 include
相反。
Prepending 对于方法覆盖、装饰和面向切面编程非常有用。它可以在修改行为的同时实现关注点的清晰分离。多个 prepend 调用以相反的顺序堆叠。
基本 prepend 示例
这个简单的例子演示了 prepend 如何改变方法查找顺序。模块方法优先于类方法。
module Greeter def greet "Hello from module!" end end class Person prepend Greeter def greet "Hello from class!" end end puts Person.new.greet
输出显示调用的是模块的方法,而不是类的方法。这是因为 prepend 将 Greeter 放在了 Person 祖先链的前面。
多次 prepend 调用
当包含多个模块时,它们会以相反的顺序插入。最后 prepend 的模块出现在祖先链中的第一个。
module A def greet "A says: #{super}" end end module B def greet "B says: #{super}" end end class Person prepend A prepend B def greet "Hello!" end end puts Person.new.greet puts Person.ancestors
输出显示 B 的方法调用 A 的方法,A 的方法调用原始方法。祖先列表显示 B 在 A 之前,A 在 Person 之前。
prepend 与 include 对比
此示例对比了 prepend 和 include。Include 将模块放在类之后,在祖先链中,而 prepend 将它们放在类之前。
module M def greet "Module says: #{super}" end end class Included include M def greet "Class says hello!" end end class Prepended prepend M def greet "Class says hello!" end end puts "Include: #{Included.new.greet}" puts "Prepend: #{Prepended.new.greet}" puts "Included ancestors: #{Included.ancestors}" puts "Prepended ancestors: #{Prepended.ancestors}"
使用 include 时,类方法获胜。使用 prepend 时,模块方法获胜。祖先输出显示了不同的方法查找顺序。
在 prepend 中使用 super
super
关键字可以自然地与 prepend 配合使用,从而实现链式方法调用。这使得强大的方法装饰模式成为可能。
module Logging def save puts "Logging before save" super puts "Logging after save" end end class Document prepend Logging def save puts "Saving document..." end end Document.new.save
Logging 模块包装了原始的 save 方法,在之前和之后添加了行为。这是用于跨关注点的常见 prepend 用例。
prepend 用于方法覆盖
prepend 可以覆盖父类的方法,而不仅仅是当前类的方法。此示例显示了覆盖继承的方法。
class Animal def speak "Animal sound" end end module Loud def speak "#{super.upcase}!!!" end end class Dog < Animal prepend Loud def speak "Woof" end end puts Dog.new.speak puts Dog.ancestors
Loud 模块覆盖了 Dog 的 speak 和 Animal 的 speak。输出显示模块的版本首先被调用,然后可以调用 super。
动态 prepend
prepend 可以在运行时调用,而不仅仅是在类定义期间。这允许动态地修改行为。
module AdminFeatures def access "Admin access granted" end end class User def access "Regular user access" end end user = User.new puts user.access User.prepend(AdminFeatures) puts user.access
在 prepend 了 AdminFeatures 之后,即使是现有实例也能获得新行为。这展示了 Ruby 的动态特性和 prepend 的运行时效果。
prepend 在实际中的应用
此示例展示了 prepend 的实际用例 - 在不修改原始类的情况下为耗时的计算添加缓存。
module Cache def calculate @cache ||= {} @cache[inputs] ||= super end def inputs [@x, @y] end end class Calculator prepend Cache def initialize(x, y) @x = x @y = y end def calculate puts "Performing expensive calculation..." @x * @y end end calc = Calculator.new(5, 10) puts calc.calculate puts calc.calculate
Cache 模块会透明地添加记忆化。第二次调用 calculate 会返回缓存的结果,而无需重新计算。原始的 Calculator 保持不变。
来源
本教程介绍了 Ruby 的 prepend 方法,并通过实际示例展示了方法覆盖、装饰和动态行为修改。
作者
列出 所有 Ruby 教程。