模組 Forwardable

Forwardable 模組提供指定方法委派至指定物件的功能,使用 def_delegatordef_delegators 方法。

例如,假設您有一個包含陣列 @records 的類別 RecordCollection。您可以提供查詢方法 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 作為繼承的替代方案。例如,以下是您如何將一系列 Array 執行個體方法新增至新類別 Queue

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

常數

FORWARDABLE_VERSION
VERSION

forwardable.rb 版本

屬性

debug[RW]

已忽略

公開執行個體方法

def_delegator(accessor, method, ali = method)
def_delegators(accessor, *methods)
def_instance_delegator(accessor, method, ali = method) 按一下以切換來源

method 定義為委派執行個體方法,並加上一個選用別名 ali。對 aliMethod 呼叫會委派至 accessor.methodaccessor 應該是方法名稱、執行個體變數名稱或常數名稱。如果提供常數名稱,請使用至常數的完整路徑。傳回已定義方法的名稱。

class MyQueue
  CONST = 1
  extend Forwardable
  attr_reader :queue
  def initialize
    @queue = []
  end

  def_delegator :@queue, :push, :mypush
  def_delegator 'MyQueue::CONST', :to_i
end

q = MyQueue.new
q.mypush 42
q.queue    #=> [42]
q.push 23  #=> NoMethodError
q.to_i     #=> 1
# File lib/forwardable.rb, line 188
def def_instance_delegator(accessor, method, ali = method)
  gen = Forwardable._delegator_method(self, accessor, method, ali)

  # If it's not a class or module, it's an instance
  mod = Module === self ? self : singleton_class
  ret = mod.module_eval(&gen)
  mod.__send__(:ruby2_keywords, ali) if RUBY_VERSION >= '2.7'
  ret
end
別名為:def_delegator
def_instance_delegators(accessor, *methods) 按一下以切換來源

定義多個委派方法的捷徑,但沒有提供使用不同名稱的規定。下列兩個程式碼範例具有相同的效果

def_delegators :@records, :size, :<<, :map

def_delegator :@records, :size
def_delegator :@records, :<<
def_delegator :@records, :map
# File lib/forwardable.rb, line 156
def def_instance_delegators(accessor, *methods)
  methods.each do |method|
    next if /\A__(?:send|id)__\z/ =~ method
    def_instance_delegator(accessor, method)
  end
end
別名為:def_delegators
委派方法 → 存取器
委派 [方法、方法、...] → 存取器
別名為:instance_delegate
instance_delegate
別名為:delegate