介紹一下 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_name
、last_name
、email_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
其實
delegate
、def_delegator
、def_delegators
分別是 instance_delegate
、def_instance_delegator
、def_instance_delegators
的別名。
source: https://github.com/ruby/ruby/blob/trunk/lib/forwardable.rb#L199-L201
另有 SingleForwardable,用法類似,差異見此。