module Forwardable

The Forwardable 模块提供了将指定方法委托给指定对象的功能,使用了 def_delegatordef_delegators 方法。

例如,假设你有一个类 RecordCollection,它包含一个数组 @records。你可以提供一个查找方法 record_number(),它简单地调用 @records 数组的 [] 方法,如下所示:

require 'forwardable'

class RecordCollection
  attr_accessor :records
  extend Forwardable
  def_delegator :@records, :[], :record_number
end

我们可以这样使用查找方法:

r = RecordCollection.new
r.records = [4,5,6]
r.record_number(0)  # => 4

此外,如果你想提供 size、<< 和 map 方法,所有这些都委托给 @records,你可以这样做:

class RecordCollection # re-open RecordCollection class
  def_delegators :@records, :size, :<<, :map
end

r = RecordCollection.new
r.records = [1,2,3]
r.record_number(0)   # => 1
r.size               # => 3
r << 4               # => [1, 2, 3, 4]
r.map { |x| x * 2 }  # => [2, 4, 6, 8]

你甚至可以扩展普通对象以包含 Forwardable

my_hash = Hash.new
my_hash.extend Forwardable              # prepare object for delegation
my_hash.def_delegator "STDOUT", "puts"  # add delegation for STDOUT.puts()
my_hash.puts "Howdy!"

另一个例子

你可以使用 Forwardable 作为继承的替代方法,当你不想继承父类的所有方法时。例如,你可以这样向新类 Queue 添加一系列 Array 实例方法:

class Queue
  extend Forwardable

  def initialize
    @q = [ ]    # prepare delegate object
  end

  # setup preferred interface, enq() and deq()...
  def_delegator :@q, :push, :enq
  def_delegator :@q, :shift, :deq

  # support some general Array methods that fit Queues well
  def_delegators :@q, :clear, :first, :push, :shift, :size
end

q = Thread::Queue.new
q.enq 1, 2, 3, 4, 5
q.push 6

q.shift    # => 1
while q.size > 0
  puts q.deq
end

q.enq "Ruby", "Perl", "Python"
puts q.first
q.clear
puts q.first

这应该会输出:

2
3
4
5
6
Ruby
nil

备注

请注意,RDoc 将不会检测到委托方法。

forwardable.rb 通过 def_delegatordef_delegators 方法提供单方法委托。有关通过 DelegateClass 进行全类委托,请参阅 delegate.rb