はじめに
あるオブジェクトが受け取ったメソッド呼び出しを、別のオブジェクトに丸投げする仕組みを移譲(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を使えば、移譲をまとめて定義できる


コメント