デメテルの法則は、オブジェクトをクラスコンストラクターに渡すことを妨げません。ただし、後で同じオブジェクトを取得し、そのオブジェクトのメソッドを呼び出してスカラー値を取得することは禁止されています。代わりに、代わりにスカラー値を返すプロキシメソッドが作成されることになっています。私の質問は、オブジェクトをクラスコンストラクターに渡すことは許容できるのに、後で同じオブジェクトを取得してそこから値を取得することは許容できないのはなぜですか?
3 に答える
デメテルの法則では、オブジェクトの外部インターフェイスを、既知のインターフェイスを持つ他の特定のオブジェクトで構成されているように見えるように設計するべきではないと定められているため、クライアントはそれを取得してアクセスできます。
オブジェクトをコンストラクターに渡して、新しいオブジェクトの動作方法を指示しますが、オブジェクトがそのパラメーターオブジェクトを保持するか、そのコピーを保持するか、または一度だけ見てそれが存在したことを忘れるかは、あなたの仕事ではありません。 。getMyParameterBackメソッドを使用することで、将来のすべての実装でオブジェクト全体をオンデマンドで生成できるようになり、すべてのクライアントが1つではなく2つのインターフェイスと結合できるようになります。
たとえば、URLパラメータをHTTPRequestオブジェクトのコンストラクタに渡す場合、それはHTTPRequestにgetURLメソッドが必要であることを意味しません。このメソッドは、呼び出し元がgetProtocol、getQueryStringなどを呼び出すことが期待されるURLオブジェクトを返します。 HTTPRequestオブジェクトを持っている人は、リクエストのプロトコルを知りたいと思うかもしれません。HTTPRequestが内部に格納していることを知っている他のオブジェクトではなく、持っているオブジェクトでgetProtocolを呼び出すことによって(法律によると)知る必要があります。
アイデアは結合を減らすことです-デメテルの法則がなければ、ユーザーはプロトコルを取得するためにHTTPRequestとURLへのインターフェースを知っている必要があります。法律では、HTTPRequestへのインターフェースのみが必要です。また、HTTPRequest.getProtocol()は、ディスカッションに関与するURLオブジェクトを必要とせずに、明らかに「http」を返すことができます。
リクエストオブジェクトのユーザーがたまたまそれを作成したユーザーであり、したがってパラメーターを渡すためにURLインターフェースも使用しているという事実は、ここにもそこにもありません。HTTPRequestオブジェクトのすべてのユーザーが自分で作成したわけではありません。したがって、法律に基づいてURLを自分で作成したためにアクセスする資格があるクライアントは、URLを要求から取得するのではなく、その方法でアクセスできます。URLを作成しなかったクライアントは作成できません。
個人的には、通常単純な形で述べられているデメテルの法則は破られていると思います。オブジェクトに文字列Nameフィールドがあり、Nameに非ASCII文字が含まれているかどうかを知りたい場合は、文字列自体を調べるのではなく、オブジェクトにNameContainsNonASCIICharactersメソッドを定義する必要があると真剣に言っていますか?文字列が私が書いた関数のパラメータであることを確認することで制限を回避するために、コールバック関数を受け取るクラスにvisitName関数を追加しますか?これはカップリングをまったく変更せず、getterメソッドをvisitorメソッドに置き換えるだけです。戻り値を操作したい場合に備えて、整数を返すすべてのクラスに算術演算の完全なセットが必要ですか?getPriceMultipliedBy(int n)?確かに違います。
それが役立つのは、それを壊したときに、なぜそれを壊しているのか、そしてそれを壊さないことによってより良いインターフェースを設計できるかどうかを自問できることです。多くの場合可能ですが、実際には、話しているオブジェクトの種類によって異なります。特定のインターフェースは、整数、文字列、さらには広く使用されている概念を表すURLなど、膨大な数のコードに対して安全に結合できます。
アイデアは、あなたがあなたの直接の友人とだけ話すということです。だから、あなたはこれをしません...
var a = new A();
var x = a.B.doSomething();
代わりにこれを行います...
var a = new A();
var x = a.doSomething(); // where a.doSomething might call b.doSomething();
呼び出し元にとっては単純になるため(Car.Start()とCar.Engine.Start())の利点がありますが、多くの小さなラッピングメソッドがあります。Mediatorパターンを使用して、このタイプの「違反」を軽減することもできます。
JPの答えはかなり良いので、これは単なる補足であり、意見の相違やその他の代替ではありません。
このヒューリスティックを理解する方法は、クラスBが変更されたために、Aへの呼び出しが中断されてはならないということです。したがって、abfoo()を使用して呼び出しを連鎖させると、AのインターフェースはBに依存するようになり、ルールに違反します。代わりに、b.foo()を呼び出すa.BFoo()を呼び出すことになっています。
これは大まかな目安ですが、依存関係を祀るほど実際には対処しない厄介なコードにつながる可能性があります。これで、BがFooを提供しなくなった場合でも、AはBFooを永久に提供する必要があります。あまり改善されておらず、少なくともいくつかのケースでは、Bへの変更が、B自体ではなく、Fooを必要とする発信者を壊した場合の方が間違いなく優れています。
また、厳密に言えば、このルールは、文字列などのユビキタスクラスの特定のグループでは常に破られています。おそらく、アプリケーションの特定のレイヤー内で同様にユビキタスであるクラスを決定し、それらのデメテルの「ルール」を自由に無視することは許容されます。