Intent
- オブジェクトをツリー構造に合成して、全体の階層を表現します。
- 再帰的合成
- “ディレクトリはエントリを含み、その各々がディレクトリとなりうる。”
- 1対多の “has a “から “is a “の階層を持つ
問題
アプリケーションでは「基本」オブジェクトおよび「複合」オブジェクトの階層的集合を処理する必要があります。 プリミティブオブジェクトの処理は一方的に処理され、コンポジットオブジェクトの処理は異なって処理される。処理を試みる前に各オブジェクトの「型」を照会しなければならないのは望ましくない。 ComponentクラスからPrimitiveクラスとCompositeクラスをサブクラス化する。
「コンポーネントを含むコンポジット、その各々がコンポジットになり得るもの」がある場合は、常にこのパターンを使用します。 残念ながら、プリミティブとコンポジットを統一的に扱うには、これらのメソッドを抽象的な Component クラスに移動する必要があります。 安全性」対「透明性」の問題については、以下の「意見」のセクションを参照してください。
Structure
Component を含む Composites (それぞれが Composite である可能性がある)。
Directories that contain files, each of which could be a directory.
Containers that contain Elements, each of which could be a Container.
例
The Composite composes objects into tree structures and lets clientstreat individual objects and compositions uniformly. 例:Composite は、ツリー構造にオブジェクトを組み合わせ、クライアントが各オブジェクトとコンポジションを統一的に扱うことを可能にします。 この例は抽象的であるが、算術式はCompositesである。 算術式は、オペランド、演算子(+ – * /)、および別のオペランドで構成されます。 オペランドは数値でもよいし、他の算術式でもよい。 したがって、2 + 3 と (2 + 3) + (4 * 6) はどちらも有効な式です。
チェックリスト
- あなたの問題が「全体-部分」の階層的関係を表すことであることが確認されています。 例えば、”Assemblies that contain components, which each of which could be an assembly.” ドメインの概念をコンテナクラスとコンテナクラスに分けます。
- コンテナとコンテナを交換可能にする “minimum common denominator” インターフェイスを作成します。
- すべてのコンテナおよびコンテナクラスは、インターフェイスに対して “is a” 関係を宣言します。
- すべてのコンテナクラスは、インターフェイスに対して 1 対多の “has a” 関係を宣言します。 残念ながら、Leaf と Composite オブジェクトを統一的に扱うために、これらのメソッドを抽象的な Component クラスに昇格させる必要があるかもしれません。 これらの “安全性” 対 “透明性” のトレードオフの議論については、Gang of Four を参照してください。
Rules of thumb
- Composite と Decorator は同様の構造図で、どちらも再帰的合成に依存して、無限の数のオブジェクトを整理しているという事実を反映しています。 Visitor は Composite に対して操作を適用することができます。 コンポジットでは、Chain ofResponsibility を使用して、コンポーネントがその親を通してグローバルプロパティにアクセスできるようにすることができます。 また、Decorator を使用して、コンポジションのパーツにこれらのプロパティをオーバーライドすることもできる。
- Composite では、再帰的な合成によって、より小さな部分から Mediator を合成することができます。 Composite の焦点は、装飾ではなく、表現にあります。 その結果、Composite と Decorator はしばしば協調して使用される。
- Flyweight はしばしば Composite と結合されて、共有リーフノードを実装する。 もし、Iterator プロトコルを提供したいのであれば、それは構いませんが、それはパターン自体の外側にあると思います。 このパターンの中心は、内部に多数のオブジェクトがあることを知る必要なく、クライアントがオブジェクトに対して操作を実行できることです。
オブジェクトの異種コレクションを原子的に(または透過的に)扱えるようにするには、「子管理」インターフェースを Composite クラス階層のルート(抽象 Component クラス)で定義することが必要です。 しかし、この選択は安全性を犠牲にします。なぜなら、クライアントがリーフ・オブジェクトからオブジェクトを追加・削除するような無意味なことをしようとするかもしれないからです。
Smalltalk の Composite パターンの実装では、通常、コンポーネントを管理するためのインターフェイスを Component インターフェイスに持たず、Composite インターフェイスに持たせる。 C++ の実装では、Component インターフェースに置くことが多いようです。 これは非常に興味深い事実で、私もしばしば考え込んでしまう。 1664>
My Component クラスは Composites が存在することを知りません。 Composites をナビゲートするためのヘルプも、Composite のコンテンツを変更するためのヘルプも提供されません。 これは、ベース クラス (およびそのすべての派生クラス) が、コンポジットを必要としないコンテキストで再利用可能であることを望んでいるためです。 ベース・クラス・ポインタが与えられたとき、それがCompositeであるかどうかを絶対に知る必要がある場合は、
dynamic_cast
を使用してこれを把握することにしています。dynamic_cast
があまりにも高価な場合は、Visitor を使用します。よくある不満。 「Composite インターフェイスを Composite クラスに押し込んだ場合、複雑な構造を列挙 (つまり、トラバース) するにはどうすればよいのでしょうか? 私の答えは、Compositeパターンで提示されるような階層に適用する動作があるとき、一般的にVisitorを使うので、列挙は問題ではない、ということだ。 Visitor は、すべてのオブジェクトに列挙インターフェイスを提供する必要はありません。
Composite は、すべての Component を Composites として扱うよう強制するものではありません。 単に、「統一的に」扱いたい操作をすべて Component クラスに入れるように指示するだけです。 add、remove、および同様の操作が一様に扱えない、あるいは扱ってはいけないのであれば、それらをComponentの基底クラスには入れないでください。 ところで、各パターンの構造図はパターンを定義しているのではなく、経験上そのパターンに共通する実現方法を示しているに過ぎないことを思い出してください。 Compositeの構造図がComponent基底クラスの子管理操作を示すからといって、そのパターンのすべての実装が同じことをしなければならないわけではありません。
Support our free website and own the eBook!
- 22のデザインパターンと8つの原則を徹底的に解説
- 406 よく構成された読みやすい専門用語のないページ
- 228 明瞭で役に立つイラストと図
- 4 言語でのコード例付きアーカイブ
- すべてのデバイスに対応しています。 EPUB/MOBI/PDF フォーマット
さらに詳しく…