「構成階層」が問題ではない場合はお詫びしますが、その意味を質問で説明します。
OO「継承階層をフラットに保つ」や「継承よりも構成を優先する」などのバリエーションに遭遇していないプログラマーはいない。しかし、深い構成階層は、上手。
実験の結果を詳述するレポートのコレクションが必要だとしましょう:
class Model {
// ... interface
Array<Result> m_results;
}
各結果には特定のプロパティがあります。これらには、実験の時間と、実験の各段階からのメタデータが含まれます。
enum Stage {
Pre = 1,
Post
};
class Result {
// ... interface
Epoch m_Epoch;
Map<Stage, ExperimentModules> m_modules;
}
わかりました。現在、各実験モジュールには、実験の結果を説明する文字列と、実験サンプルセットへの参照のコレクションがあります。
class ExperimentalModules {
// ... interface
String m_reportText;
Array<Sample> m_entities;
}
そして、各サンプルには...
問題は、アプリケーションドメインからオブジェクトをモデリングしている場合、これは非常に自然な適合のように見えますが、結局のところ、Result
はデータのばかばかしいコンテナーにすぎません。そのためのクラスの大きなグループを作成する価値はないようです。
上記のデータ構造とクラスがアプリケーションドメインの関係を正しくモデル化していると仮定すると、深い構成階層に頼らずに、このような「結果」をモデル化するより良い方法はありますか?そのようなデザインが良いものかどうかを判断するのに役立つ外部コンテキストはありますか?
これが デメテルの法則/最小の知識の原則 についてです。 Model
のユーザーは、ExperimentalModules
'インターフェースについて必ずしも知っている必要はありません。
一般的な問題は、クラスが別の型から継承するか、何らかの型のパブリックフィールドを持っている場合、またはメソッドが型をパラメーターとして取るか、型を返す場合、それらすべての型のインターフェイスが、クラスの有効なインターフェイスの一部になることです。問題は次のとおりです。これらが依存する型:クラスの全体のポイントを使用していますか?それとも、私が誤って公開した実装の詳細だけですか?それらが実装の詳細である場合、私はそれらを公開し、私のクラスのユーザーがそれらに依存することを許可したので、私のクライアントは私の実装の詳細に密接に結合されています。
したがって、まとまりを改善したい場合は、ユーザーにプライベート構造に到達するよう要求する代わりに、有用な処理を行うメソッドをさらに記述することで、この結合を制限できます。ここでは、結果を検索またはフィルタリングするメソッドを提供する場合があります。これにより、インターフェイスを壊すことなく内部表現を変更できるため、クラスのリファクタリングが容易になります。
しかし、これにはコストがないわけではありません。公開されたクラスのインターフェイスは、常に必要とは限らない操作で肥大化しています。これは、インターフェース分離の原則に違反します。ユーザーが実際に使用するよりも多くの操作に依存するように強制するべきではありません。そして、それはdesignが重要な場所です:デザインは決定原則ですあなたの文脈では、そして適切な妥協点を見つけることについてより重要です。
複数のバージョン間でソースの互換性を維持する必要のあるライブラリを設計するときは、知識を最小限に抑えるという原則に、より注意深く従う傾向があります。ライブラリが内部で別のライブラリを使用している場合、そのライブラリで定義されているものをユーザーに公開することはありません。内部ライブラリからインターフェイスを公開する必要がある場合は、単純なラッパーオブジェクトを作成します。ブリッジパターンやファサードパターンなどのデザインパターンが役立ちます。
しかし、インターフェイスが内部でのみ使用されている場合、または公開するインターフェイスが非常に基本的である場合(標準ライブラリの一部、またはライブラリを変更しても意味がないほど基本的である場合)、非表示にしても役に立たない場合があります。これらのインターフェース。いつものように、万能の答えはありません。
「深い構成階層」も悪いのではないですか?
もちろん。ルールは実際には「すべての階層を可能な限りフラットに保つ」と言う必要があります。
しかし、深いコンポジション階層は深い継承階層よりも脆弱ではなく、変更するのにより柔軟です。直感的には、これは当然のことです。家族の階層も同じです。血縁者との関係は遺伝学で固定されています。友達や配偶者との関係はそうではありません。
あなたの例は私を「深い階層」とは思わない。フォームは、x座標とy座標から継承する点のセットから継承する長方形から継承します。Steamの完全版はまだ入手していません。
アインシュタインの言い換え:
すべての階層を可能な限りフラットに保ちますフラットではありません
あなたがモデル化している問題がそのようなものである場合、それをそのままモデル化することに我慢するべきではありません。
アプリは単なる巨大なスプレッドシートだったので、そのままコンポジション階層をモデル化する必要はありません。そうでなければそれを行います。ものすごく深いとは思いません。コレクションを生成するのにコストがかかる場合は、コレクションを遅延して返すゲッターを保持してください。怠惰ということは、必要になるまでデータを入力しないことです。
はい、リレーショナルテーブルのようにモデル化できます
Result {
Id
ModelId
Epoch
}
多くの場合、プログラミングが簡単になり、データベースへのマッピングが簡単になります。しかし、あなたの関係のハードコーディングを失うことを犠牲にして
Javaはすべてがオブジェクトであるという考えに基づいて構築されているので、Javaでこれを解決する方法は本当にわかりません。中間クラスを辞書で置き換えることにより、中間クラスを抑制することはできません。データBLOBのタイプは異種です(タイムスタンプ+リスト、または文字列+リストなど)。コンテナクラスをそのフィールドで置き換える(たとえば、「m_Epoch」変数を「m_modules」で渡す)代わりの方法は、暗黙のオブジェクトを作成しているため(他のオブジェクトがないと作成できません)、特に利点はありません。
私の好みの言語(Python)では、特定のロジック(基本的な取得と設定を除く)が属していない場合は、オブジェクトを作成しないというのが私の経験則です。しかし、あなたの制約を考えると、あなたが持っているコンポジション階層は可能な限り最高のデザインだと思います。