コンポジションと委譲は、どちらも『あるオブジェクトが別のオブジェクトの機能を使う』という文脈で登場するため、混同しやすい概念です。
しかし、この2つは同じ意味ではありません。
本記事では、この両者の関係性について説明します。
コンポジション
コンポジション(Composition)とは、オブジェクトが別のオブジェクトを部品として持つことで、全体としての機能を作り上げるという設計思想です。
これは「has-a」(〜を持っている)の関係で表現されます。
例えば、車はエンジンやタイヤなど、複数の部品から成り立っています。
車のクラスを設計するとき、エンジンやタイヤのクラスを継承するのではなく、それらのインスタンスを「部品」として持つように実装します。
これにより、部品の再利用や交換が容易になります。
class Engine
def start
puts "エンジンをかけます。"
end
end
class Car
def initialize
# CarはEngineを「持っている」
@engine = Engine.new
end
end
このコードは、Car
クラスがEngine
クラスをコンポジションしています。
委譲
委譲(Delegation)とは、あるオブジェクトが受け取ったメソッド呼び出しを、保持している別のオブジェクトに任せるという、具体的なプログラミングのテクニックです。
Car
クラスに「エンジンをかける」というstart
メソッドを追加したいとします。
しかし、実際にエンジンをかける処理を行うのは、Car
クラスが持っているEngine
オブジェクトです。
ここで登場するのが「委譲」です。Car
クラスのstart
メソッドの中で、Engine
オブジェクトのstart
メソッドを呼び出します。
class Car
def initialize
@engine = Engine.new
end
# Engineオブジェクトにstartメソッドの処理を「委譲」
def start
@engine.start
end
end
car = Car.new
car.start # => "エンジンをかけます。"
このコードは、Car
のstart
メソッドがEngine
のstart
メソッドに処理を委譲しています。
DIと委譲
委譲は、コンポジションと組み合わせるだけではありません。
例えば DI(依存性注入)と組み合わせて使われることもあります。
DI は「依存するオブジェクトを外部から注入する設計」です。
class Car
def initialize(engine)
# Engineを自分で生成せず、外部から受け取る(=DI)
@engine = engine
end
def start
# start処理はEngineに任せる(=委譲)
@engine.start
end
end
car = Car.new(Engine.new)
car.start # => "エンジンをかけます。"
このコードは、Car
クラスがEngine
を自分でnew
せず、外部から注入しています。
このように、委譲とは「別のオブジェクトに処理を任せるテクニック」です。
まとめ
- コンポジション:設計の考え方。「has-a」の関係でオブジェクト同士を組み合わせる
- 委譲:実装のテクニック。別のオブジェクトに処理を任せる
コメント