下の図に示すように、ツリーのような構造です(1つの小さな例として)。ツリーは、次の2つの異なるノードタイプで構成されています。
Id
およびType
属性をData Nodes
と共有しており、子の順序付きリストも保持しています。このツリーは1つのコンポーネントで作成され、操作(保存、GUIでの表示など)のために別のシステムに送信されます。このツリーを設計するには、以下で説明するさまざまなアプローチがあり、その長所と短所があります。それらについてどう思いますか、この場合、どれが最も適切ですか?
アプローチ1:共通の機能(例:getId
、getType
)のインターフェースを用意し、特定の機能(例: getChildren
)具象クラスで。このように、ツリーを操作する必要のある他のアプリケーション(GUIで保存または表示するため)は、ノードのタイプに基づいて多くのダウンケースを必要とします。
アプローチ2:両方のノードタイプのすべての機能に対して1つのインターフェイスを用意します。各具象クラスで、メソッドを実装するか(該当する場合)、例外をスローします。ノードのタイプに基づいて適切なメソッドを呼び出すのは、ユーザーの責任です。
アプローチ3:インターフェイスにアクセスしないでください。ただし、両方のノードタイプのすべてのデータ+メソッドを含む1つのクラス(例:TreeNode
)のみがあります。ここでも、適切なオブジェクトタイプで適切なメソッドを呼び出すのはユーザーの責任です。
p.s。必要に応じて、実装言語はC++ 11になります。
更新1:このツリーの詳細と、より具体的な要件を以下に示します。
あなたが言及している3つ以外の別のオプションは、データを独自のクラスに分離し、オプションの方法でそれと子の両方を提供する基本的なノードインターフェイスを持つことです。
子にとって、これは簡単です。子のないノードには、子がまったくありません。
データの場合、戻り値の型として_std::optional<NodeData&>
_を使用するか、2つのメソッド(hasNodeData、getNodeData)を使用できます。
これはまだそこにデータがあるかどうかを確認するために他のコードを必要とします(それを回避する方法はありません、AFAIK)。
それは完璧ではありません-最上位のインターフェースにNodeDataを含めているので、さまざまなノードタイプがあり、すべてが異なるデータを持っている場合、これは扱いにくくなる可能性があります。しかし、あなたが説明する場合のために、これはかなりクリーンなアプローチIMOでしょう。
。
任意の数のタイプに対応できるアプローチは、そのタイプのデータに対してstd :: optionalを返すテンプレート化されたgetDataメソッドです。
例:if(auto fooData = mysteryNode.getData<FooData>()) ...
この状況ではやり過ぎかもしれませんが、最も一般的なインターフェースになります。
アプローチ1と2はどちらも有効です。最も適切なものを選択するための重要な問題は、これらのオブジェクトをクライアントコンテキスト(GUI、永続性など)でどのように均一に処理するかです。 複合デザインパターン は、次のことを目的としているため、役立つ場合があります。
オブジェクトをツリー構造に構成して、部分全体の階層を表します。 Compositeを使用すると、クライアントは個々のオブジェクトとオブジェクトの構成を均一に処理できます。
主な問題は、最小限の一般的な動作に統一された処理が必要か(アプローチ1)、またはより野心的であると予想されるか(アプローチ2)です。 GoF この問題について2つの側面(167ページ)で説明します。
したがって、完璧な解決策はありません。それは、あなたの意図を考慮して見つけるのは微妙なバランスです。あなただけがそれを見つけることができます。
実は確かです。アプローチ3は避けなければなりません。これは、懸念の健全な分離と オープン/クローズの原則 に反します。この設計は、時間の経過とともに維持するのが困難になるモノリシックコードにつながります。たとえば、ある日、さまざまな種類のコレクションまたはさまざまな種類のデータノードが必要であるという結論に達したとします。アプローチ1や2では大きな問題にはなりません。しかし、アプローチ3では、すべてを確認する必要があります。
ノードクラスの1つのタイプのみが表示されます:ノード。ノードのリストであるプロパティChildNodesがあります。これは空でもかまいません。 Nodeにはnullの可能性があるプロパティDataもあります。Dataはデータプロパティを含む別のクラスです。
コレクションノードとは、子を持ち、データを持たないノードです(Data == null)。データノードには、0個の子と1つのDataオブジェクトがあります。あなたは両方を持つことができます(なぜないのですか?)。それが気に入らない場合は、子がデータノードに追加されるか、データがコレクションノードに追加されるとすぐにスローします。