Ruby 標準庫 Forwardable

Juanito FatasThinking about what to do.

介紹一下 Ruby 的標準庫: Forwardable原始碼

轉發,轉發“訊息”給別的物件。

假設今天有一個 User,有一個 Account 專門存放 User 的資料(“collaborator object”):

Account = Struct.new(:first_name, :last_name, :email_address)

class User
  attr_reader :account

  def initialize(account)
    @account = account
  end

  def first_name
    account.first_name
  end

  def last_name
    account.last_name
  end

  def full_name
    "#{first_name} #{last_name}"
  end

  def email_address
    account.email_address
  end
end

上面的程式碼有許多重複之處,有三個方法都用到了 account,此時就可以使用 Forwardable 來簡化:

require 'forwardable'

class User
  attr_reader :account

  extend Forwardable

  def_delegators :account, :first_name, :last_name, :email_address

  def initialize(account)
    @account = account
  end

  def full_name
    "#{first_name} #{last_name}"
  end
end

first_namelast_nameemail_address 都“轉發”給 account

User 不想對外開放 account 這個 attr_reader,還可以:

require 'forwardable'

class User
  extend Forwardable

  def_delegators :@account, :first_name, :last_name, :email_address

  def initialize(account)
    @account = account
  end

  def full_name
    "#{first_name} #{last_name}"
  end
end

def_delegator(accessor, method, ali = method) 一次只能“轉發”一個方法,第三個參數是(可選的)別名。

def_delegators(accessor, *methods) 一次可以“轉發”多個方法。

require 'forwardable'

class User
  extend Forwardable

  def_delegator :@account, :first_name, :nickname
  def_delegators :@account, :last_name, :email_address

  def initialize(account)
    @account = account
  end

  def full_name
    "#{first_name} #{last_name}"
  end
end

會先對 '@bank.account' 做求值:

def_delegator '@bank.account', :balance

第三種形式使用 delegate,接受的是 Hash。

delegate :method1 => accessor
delegate [:method1, :method2] => accessor

其實

delegatedef_delegatordef_delegators 分別是 instance_delegatedef_instance_delegatordef_instance_delegators 的別名。

source: https://github.com/ruby/ruby/blob/trunk/lib/forwardable.rb#L199-L201

另有 SingleForwardable,用法類似,差異見此