69

デメテルの法則は、自分が直接知っている対象にのみ話しかけるべきであることを示しています。つまり、他のオブジェクトと通信するためにメソッド チェーンを実行しないでください。これを行うと、中間オブジェクトとの不適切なリンクが確立され、コードが他のコードに不適切に結合されます。

良くないね。

解決策は、あなたが知っているクラスが、関係のあるオブジェクトに責任を委任する単純なラッパーを本質的に公開することです。

それは良い。

でも、それだとクラスの結束が弱くなるらしい。もはや、それが何をするかについて正確に責任を負うだけではなく、関連するオブジェクトのインターフェースの一部を複製することによって、ある意味でコードのまとまりをなくすデリゲートも持っています。

良くないね。

それは本当に結束力を低下させるのでしょうか?それは2つの悪の小さい方ですか?

これは開発の灰色の領域の 1 つで、境界線がどこにあるかを議論できる場所ですか、それとも、どこに境界線を引くか、その決定を下すために使用できる基準を決定するための強力で原則に基づいた方法はありますか?

4

6 に答える 6

49

「オブジェクト指向の分析と設計」の Grady Booch:

「結束の考え方は、構造化された設計にも由来します。簡単に言えば、結束は、単一のモジュール (およびオブジェクト指向設計の場合、単一のクラスまたはオブジェクト) の要素間の接続の程度を測定します。結束の最も望ましくない形式は偶然です。まったく無関係な抽象化が同じクラスまたはモジュールにスローされる凝集力. たとえば, 動作がまったく無関係な犬と宇宙船の抽象化を含むクラスを考えてみましょう. 凝集力の最も望ましい形は機能的凝集力です.クラスまたはモジュールのすべてが連携して、適切に制限された動作を提供します。したがって、そのセマンティクスが犬、犬全体、および犬以外の行動を包含する場合、クラス Dog は機能的にまとまりがあります。」

上記の Dog を Customer に置き換えると、少しわかりやすくなるかもしれません。したがって、実際の目標は、機能的な結束を目指し、偶然の結束から可能な限り離れることです. 抽象化に応じて、これは単純な場合もあれば、リファクタリングが必要な場合もあります。

凝集性は、単一のクラス、つまり一緒に動作するクラスのグループよりも「モジュール」に適用されることに注意してください。したがって、この場合、Customer クラスと Order クラスは、この強力な関係 (顧客が注文を作成し、注文が顧客に属している) を持っているため、適切なまとまりを持っています。

Martin Fowler は、これを「デメテルの提案」と呼んだ方が快適だと述べています (モックはスタブではないという記事を参照してください)。

「モックスト テスターは、『列車事故』を回避することについてもっと話します。つまり、getThis().getThat().getTheOther() のスタイルのメソッド チェーンです。メソッド チェーンを回避することは、デメテルの法則に従うこととしても知られています。メソッド チェーンは臭いですが、仲介者のオブジェクトが転送方法で肥大化するという反対の問題も臭いです. (私はいつも、デメテルの提案と呼ばれていれば、デメテルの法則の方が快適だと感じていました.)

それは私がどこから来ているのかをうまくまとめています。それは完全に受け入れられ、多くの場合、「法律」を厳密に順守するよりも低いレベルの結束が必要です。偶然の結束を避け、機能的な結束を目指しますが、設計の抽象化により自然に適合するために必要な微調整に夢中にならないでください。

于 2008-10-03T05:47:55.263 に答える
21

あなたが持っていることによってデメテルの法則に違反している場合

int price = customer.getOrder().getPrice();

解決策は、getOrderPrice() を作成してコードを次のように変換することではありません。

int price = customer.getOrderPrice();

代わりに、これはコードの匂いであることに注意し、関連する変更を加えて、結束を高め、結合を低下させることを願っています。残念ながら、ここには常に適用される単純なリファクタリングはありませんが、おそらく、tell don't ask を適用する必要があります。

于 2008-10-02T15:53:01.760 に答える
6

結束の意味を誤解している可能性があると思います。他のいくつかのクラスの観点から実装されているクラスは、明確な概念を表し、明確な目的を持っている限り、必ずしも凝集度が低いとは限りません。たとえば、クラス(生年月日)、、および(その人が通った学校のリスト)class Personに関して実装されている があるとします。生年月日、その人が最後に通った学校、または住んでいる州を取得するためのラッパーを提供して、それらの他のクラスに関して実装されているという事実を公開しないようにすることができます。これによりカップリングは減少しますが、凝集性は低下しません。DateAddressEducationPersonPersonPerson

于 2008-10-02T16:00:43.660 に答える
4

グレーゾーンです。これらのプリンシパルは、作業を支援することを目的としています。それらのために作業していることがわかった場合 (つまり、それらが邪魔をしている、および/またはコードが複雑になっていることがわかった場合)、準拠しすぎているため、後退する。

それをあなたのために働かせてください、それのために働かないでください。

于 2008-10-02T16:01:42.597 に答える
1

これが実際に結束力を低下させるかどうかはわかりません。

集約/合成は、クラスが他のクラスを利用して、そのパブリック メソッドを通じて公開するコントラクトを満たします。クラスは、関連するオブジェクトのインターフェースを複製する必要はありません。実際には、これらの集約されたクラスに関する知識をメソッドの呼び出し元から隠しています。

複数レベルのクラス依存関係の場合にデメテルの法則に従うには、各レベルで集約/構成と適切なカプセル化を適用するだけで済みます。

つまり、各クラスには他のクラスへの 1 つ以上の依存関係がありますが、これらは参照先のクラスへの依存のみであり、プロパティ/メソッドから返されるオブジェクトへの依存はありません。

于 2008-10-02T16:00:17.110 に答える
0

カップリングと結合の間にトレードオフがあると思われる状況では、おそらく「他の誰かがこのロジックを既に書いていて、そのロジックのバグを探していたら、どこを最初に探すだろうか?」と自問するでしょう。そのようにコードを書きます。

于 2008-10-02T15:45:17.693 に答える