概要
Composite(コンポジット)パターンは、
個と集合を同一のインターフェースで扱えるようにする設計パターンである。
ツリー構造(階層構造)を表現し、葉(Leaf)と枝(Composite)を一貫した操作で処理可能にする。
GUI、ファイルシステム、HTML構文木、数式木など、再帰的な構造を扱う領域において極めて有効。
1. なぜCompositeが必要か?
❌ ノードとコンテナで異なるロジックが必要になり、分岐処理が煩雑になる
if isinstance(node, Folder):
for child in node.children:
...
else:
process(node)
→ 個別に処理を書く必要があり、拡張性が著しく低下
✅ 個(Leaf)と集合(Composite)を共通インターフェースで扱えるようにする
root.render()
→ 再帰構造を意識せず、構成要素を一貫して操作可能
2. 基本構造
✅ Component(共通インターフェース)
class Graphic:
def render(self):
raise NotImplementedError
✅ Leaf(個体)
class Circle(Graphic):
def render(self):
print("● Circleを描画")
✅ Composite(集合)
class Group(Graphic):
def __init__(self):
self._children = []
def add(self, graphic: Graphic):
self._children.append(graphic)
def render(self):
print("Group開始")
for child in self._children:
child.render()
print("Group終了")
✅ 使用例
circle1 = Circle()
circle2 = Circle()
group = Group()
group.add(circle1)
group.add(circle2)
root = Group()
root.add(group)
root.add(Circle())
root.render()
出力:
Group開始
Group開始
● Circleを描画
● Circleを描画
Group終了
● Circleを描画
Group終了
3. Python的応用:__iter__
と yield from
によるツリーのトラバース
class Group(Graphic):
def __iter__(self):
for child in self._children:
if isinstance(child, Group):
yield from child
else:
yield child
→ 再帰構造をfor文で簡潔に処理可能にする
4. 実務ユースケース
✅ UIコンポーネントツリー(ボタン、ラベル、パネル)
→ 各コンポーネントを統一されたrender()で描画処理
✅ ファイルシステム(ファイルとフォルダ)
→ ファイルもフォルダも共通インターフェースで操作可能
✅ 数式木・構文解析(AST)
→ 項と演算子のノードを一貫して評価・解析
✅ メニュー構造(ネストされたメニュー・サブメニュー)
→ 表示・切替など動作を共通化
5. よくある誤用と対策
❌ コンポジットオブジェクトがリーフに依存しすぎて設計が崩れる
→ ✅ 共通インターフェースを厳密に守り、各役割に責務を限定する
❌ コンポジットがリーフ以外も自由に受け入れてしまう
→ ✅ 子要素は Graphic
インターフェースを実装しているか確認する
❌ リーフに不要な操作(add/remove)を持たせてしまう
→ ✅ リーフ側では未実装で NotImplementedError
を返すのが自然
結語
Compositeパターンとは、“個と集合を統一的に扱い、再帰構造を抽象化する設計”である。
- ツリー構造をシンプルにモデル化し、操作を一貫性あるAPIに統合
- 再帰的な要素に対して、拡張性・保守性・可読性の高い構造が構築可能
- Pythonではダックタイピングと再帰生成で、コンパクトで直感的に表現可能
Pythonicとは、“違いを意識せず一貫した操作を実現すること”。
Compositeパターンはその統一の知性を、階層構造に注ぎ込む技法である。