ようこそ。睡眠不足なプログラマのチラ裏です。

デザインパターン第6回「Compositeパターン」

今回はGoFデザインパターンの中から
「Compositeパターン*1」をぬるーく解説します。


オブジェクト指向開発を進めていくと、このパターンの考え方が必要となる場面は多いだろう。
つまり、プリミティブなオブジェクトと、コンテナオブジェクトを一様に扱いたいような場面だ。
例えば、ツリー構造を操作するプログラムを設計するのであれば、ほぼ100%このパターンを用いると考えて間違いない。


■Compositeパターンの概要■
Compositeとは、英語で「複合物」、あるいは「合成物」を意味する言葉です。


Compositeパターンでは、複数のオブジェクトを合成します。
そして、その合成されたオブジェクトと単一オブジェクトを同一視するのが特徴です。


このパターンを用いると、ディレクトリのような、ツリー構造を伴う
再帰的なデータ構造を容易に表現することができます。
Compositeパターンにおいて登場するオブジェクトは、「枝」と「葉」であり、
これらは共通のインターフェイスを実装します。したがって、枝と葉を同様に扱えるというメリットがある。
つまりこのパターンは、プリミティブなオブジェクトであっても、
複合的に構成されたコンテナオブジェクトであっても、一様に扱うことができます。


さて、オブジェクト指向開発においては、プリミティブなオブジェクトを複数組み合わせる事で、
複雑なオブジェクトを構成するような場面が多々あります。
例えば、大規模なゲーム開発などでは、当然オブジェクト指向開発が主流です。



ここでは、Compositeパターンを適用する例として「かまいたちの夜」のような、
サウンドノベルというジャンルのゲームについて考えてみる。


サウンドノベルというジャンルのゲームは、シーンの連続で構成されています。
また、シーンの中には更に詳細なシーンがいくつか含まれていたりします。
複数ある選択肢からPlayerが選んだものに応じて、次々とシーンが分岐していきます。
グッドシーンA、グッドシーンB、バッドシーンA、バッドシーンB、ゲームオーバーシーン・・・。
グッドシーンAには、更に詳細なシーン1、シーン2、シーン3などが含まれています。
このように、シーンは再帰的に繰り返されていると言えます。
この例では、分岐点となるシーンが「枝」であり、
ゲームオーバーシーンや、エンディングシーンなどが「葉」に相当します。





例えば、ゲームオーバーシーンのようなLeafSceneクラスでは、Beginメソッドによってシーンが開始されるとします。
その代わり、子シーンを追加したり取り除いたりする機能はありません。
一方、分岐点となるようなCompositeSceneクラスでは、Beginメソッドによってシーンが始まるだけでなく、
例えば保持している子シーンの分岐画面になるとか、親シーンまでの分岐情報によって
特定の子シーンを選択できないようにしたり、特定の子シーンのBegin関数を強制的に呼び出したりするなどします。
つまりこれは、CompositeSceneクラスとLeafSceneクラスの合成により、
すべてのシーンについて管理と制御が可能となることを意味します。



スーパーファミコン版「かまいたちの夜」からプレイステーション版「かまいたちの夜」に
新たに搭載された「フローチャート機能」では、一度見たシーンが自動的に記録され、
一度見たシーンを自由に行き来することができるようなった。
また、まだ選んでいない選択肢について色分けがされるようにもなった。
仮に「かまいたちの夜」がシーンについて、Compositeパターンを適用していたのであれば、
この機能を追加することは、実に容易だったであろうことが想像できる。


Compositeパターンのデメリット

最後に、Compositeパターンのデメリットについても言及しておく必要があるだろう。
抽象クラスは、自らの子孫である「枝」と「葉」クラスを同一視(ポリモーフィズム)するために、
互いの最大公約数となるインターフェイスを持つことになる。
つまり、Compositeクラスでは実装が必要なメソッドであっても、Leafクラスには
実装の必要のないメソッドが、インターフェイスに定義されるということになる。
Leafクラスは子ノードを保持しないので、LeafクラスのAddメソッドやRemoveメソッドでは
当然、例外をスローするなどの考慮が必要となります。
これは所謂、インターフェイスの最大化と呼ばれる好ましくない設計の1つなのだが、
これが必ずしも悪とは言い切れない。オブジェクト指向設計のジレンマというか、
まあ、機能と実装のトレードオフは避けられないので、ケースバイケースで対処するしかないですな(´・ω・`)



余談となるが、このパターンを解説する際に、メタファとして「マトリョーシカ」を例とする人がいるようだけど、
マトリョーシカだと物理的に小さなものしかCompositeに内包できないわけで、どうも具合が悪い。
確かにマトリョーシカのその様は、パターンの性質をイメージし易い感じはあるのだけど、
Compositeパターンの考え方を初導入する人にとっては、かえって混乱を招きかねない気がする。


結構面倒くさかったけど作ったオマケ
Compositeパターン - VB.NET - 雑なサンプルソース

*1:CompositeパターンはGoFデザパタの中でも、とりわけ定番のパターン。