はじめに
あるオブジェクトが受け取ったメソッド呼び出しを、別のオブジェクトに丸投げする仕組みを移譲(Delegation)と呼びます。
移譲の基礎
User
クラスがProfile
クラスを持ち、Profile
のname
を取得するケースを考えます。
class Profile
attr_reader :name
def initialize(name)
@name = name
end
end
class User
def initialize(profile)
@profile = profile
end
def name
@profile.name # 呼び出しをそのままprofileに移譲
end
end
profile = Profile.new("Tanaka")
user = User.new(profile)
puts user.name # => "Tanaka"
これが移譲の基本です。
このようにuser.name
と書くと、裏側では profile.name
が呼ばれるようにするのが移譲です。
手書き移譲の欠点
例えばProfile
からname
だけでなくage
やemail
も取りたい場合…
class User
def initialize(profile)
@profile = profile
end
def name
@profile.name
end
def age
@profile.age
end
def email
@profile.email
end
end
同じようなコードを何度も書くことになります。
この繰り返しは保守性を下げ、修正漏れの原因にもなります。
※なお、この例ではProfile
クラスにage
およびemail
の定義を追加する必要があります。
Forwardableによる効率化
Rubyには標準でForwardable
モジュールがあり、このような移譲を簡単に書くことができます。
require 'forwardable'
class User
extend Forwardable
def_delegators :@profile, :name, :age, :email
def initialize(profile)
@profile = profile
end
end
これだけで、
user.name
user.age
user.email
を使えるようになります。
Forwardableのポイント
extend Forwardable
でクラスメソッドとしてdef_delegators
が使えるようになる。def_delegators
の第一引数に移譲先オブジェクト(ここでは@profile
)を指定し、第二引数以降に移譲したいメソッド名を並べる。
まとめ
- 移譲とは、メソッド呼び出しを別のオブジェクトにそのまま渡す仕組み
- 純粋なRubyでも簡単に実装できるが、メソッド数が増えると面倒
- Ruby標準の
Forwardable
を使えば、移譲をまとめて定義できる
コメント