デメテルの法則によれば、クラスはそのメンバーの1人を返すことを許可されていますか?


13

デメテルの法則に関して3つの質問があります。

別に特にリターンオブジェクトに任命されたクラスから-などの工場やビルダークラスなど-それは、例えば、クラスのプロパティの1つが保持しているか、それが違反するオブジェクト、オブジェクトを返すメソッドのために大丈夫ですデメテルの法則(1) ?そして、それがデメテルの法則に違反する場合、返されるオブジェクトがデータの一部を表し、このデータのゲッターのみを含む不変オブジェクトであるかどうかは問題になりますか?または、そのようなValueObjectはそれ自体がアンチパターンですか?そのクラスのデータで行われていることはすべてクラスの外部で行われているためです(2b)?

擬似コードで:

class A {}

class B {

    private A a;

    public A getA() {
        return this.a;
    }
}

class C {

    private B b;
    private X x;

    public void main() {
        // Is it okay for B.getA to exist?
        A a = this.b.getA();

        a.doSomething();

        x.doSomethingElse(a);
    }
}

デメテルの法則は、上記のようなパターンを禁じていると思います。doSomethingElse()法律に違反せずに呼び出すことができるようにするにはどうすればよいですか(3)。


9
多くの人々(特に機能的なプログラマー)は、デメテルの法則をアンチパターンと見なしています。他の人はそれを「デメテルの時折有用な提案」と見ています。
デビッドハンメン

1
デメテルの法則の不条理を示しているので、この質問に賛成します。はい、LoDは「時々便利」ですが、通常は愚かです。
user949300

どのようxに設定されていますか?
-candied_orange

@CandiedOrange xは、単に値を呼び出すことができるオブジェクトである異なるプロパティですdoSomethingElse
user2180613

1
あなたの例はLODに違反していません。LODは、オブジェクトのクライアントがそのオブジェクトの内部詳細または共同作業者に結合するのを防ぐことです。のようなものを避けたいuser.currentSession.isLoggedIn()。なぜなら、それはクライアントuserだけでuserなく、その協力者にも結びつくからsessionです。代わりに、あなたが書くことができるようにしたいuser.isLoggedIn()。これは通常、実装をに委譲するisLoggedInメソッドを追加することuserで実現されcurrentSessionます。
ヨナ

回答:


7

多くのプログラミング言語では、返される値はすべてオブジェクトです。他の人が言ったように、返されたオブジェクトのメソッドを使用できないと、何も返されなくなります。「クラスA、B、Cの責任は何ですか?」と自問するべきです。これが、A、B、Cのようなメタ構文変数名を使用すると、常に「依存する」という回答を求める理由です。これらの用語には継承責任がないためです。

ドメインドリブンデザインを検討することをお勧めします。これにより、機能がどこに行き、誰が何を呼び出すべきかを判断するための、より微妙な一連のヒューリスティックが得られます。

不変オブジェクトに関する2番目の質問は、エンティティ
オブジェクトと比較した値オブジェクトの概念です。DDDでは、ほとんど制限なしで値オブジェクトを渡すことができます。これは、エンティティオブジェクトには当てはまりません。

Aggregatesにアクセスするためのルールとして、単純化されたLODはDDDではるかによく表現されます。外部オブジェクトは、集計のメンバーへの参照を保持しないでください。ルート参照のみを保持する必要があります。

DDDは別として、少なくとも、ドメインに適した独自のステレオタイプのセットをクラスに開発する必要があります。システムを設計する際に、これらのステレオタイプに関する規則を施行します。

また、これらのルールはすべて、複雑さを管理するためのものであり、自分自身を制約するものではないことを常に忘れないでください。


1
DDDは、データなどのクラスメンバーを公開できる(つまり、ゲッターが存在する)場合についてのルールを提供していますか?すべての周りの議論Law of Demetertell, don't askgetters are evilobject encapsulationおよびsingle responsibility principleこの質問に煮詰めるように見える:オブジェクトの値を取得し、それを使って何かをすることとは対照的に、ときドメインオブジェクトへの委任を使用する必要がありますか?そのような決定が行われなければならない状況に微妙な資格を適用できると非常に便利です。
user2180613

多くの人がクラスインスタンスをデータ構造(渡されるデータの袋)として扱うため、「ゲッターは悪」というようなガイドラインが存在します。これはオブジェクト指向プログラミングではありません。オブジェクトは、実際に何らかの作業を提供する機能/動作のバンドルです。仕事の範囲は責任によって定義されます。この作業により、メソッドなどから返されるオブジェクトが明らかに作成されます。クラスが「属性YおよびZを持つデータオブジェクトXを表す」を超えて明確に定義できる意味のある作業を実際に実行している場合、正しい道を進んでいます。私はそれを強調しません。
ジェレミー

その点を拡張するために、多くのゲッター/アスカーを使用する場合、それは通常、ファウラーがトランザクションスクリプトと呼ぶ、すべてをオーケストレーションしている大きなメソッドがあることを意味します。ゲッターの使用を開始する前に、自問してください...オブジェクトからこのデータを要求するのはなぜですか?オブジェクトのメソッドにこの機能がないのはなぜですか?この機能を実装することがこのクラスの責任ではない場合は、ゲッターを使用してください。Fowlerによる本[Refactoring](martinfowler.com/books/refactoring.html)は、このような質問の発見的方法に関する本です。
ジェレミー

Vaughn Vernonの著書「Implementing Domain-Driven Design」で、Demeter and Tellの法則について言及しています(10章、382ページで質問しないでください)。
ジェレミー

6

デメテルの法則によれば、クラスはそのメンバーの1人を返すことを許可されていますか?

はい、最も確かです。

主なポイントを見てみましょう:

  • 各ユニットは、他のユニットに関する限られた知識のみを持つ必要があります。現在のユニットに「密接に」関連するユニットのみです。
  • 各ユニットは、その友人とのみ会話する必要があります。見知らぬ人と話をしないでください。
  • 身近な友達とだけ話してください。

これら3つすべてから、1つの質問をすることができます 。友達は誰ですか?

何を返すかを決定する際、デメテルの法則または最少知識の原則(LoD)は、違反を主張するコーダーを防御することを指示するものではありません。コーダーに強制的に違反させないよう指示します。

これらを混乱させることがまさにセッターが常にvoidを返さなければならないと考える理由です。いいえ。システムの状態を変更しないクエリ(ゲッター)を作成する方法を許可する必要があります。これが基本的なコマンドクエリの分離です。

これは、あなたが望むものを一緒に連鎖するコードベースを自由に掘り下げることができるということですか?いいえ。一緒にチェーンすることを意図したものだけを一緒にチェーンします。そうしないと、チェーンが変更され、突然あなたのものが壊れる可能性があります。これが友達の意味です。

長いチェーンを設計できます。流interfacesなインターフェース、iDSL、Java8の大部分、そして古き良きStringBuilderはすべて、長いチェーンを構築するためのものです。チェーン内のすべてが連携して動作することを意図しており、連携し続けることを約束されているため、彼らはLoDに違反しません。互いに聞いたことのないものをつなぎ合わせると、デメテルに違反します。友人とは、あなたのチェーンが機能し続けることを約束した友人です。Friends of Friendsはしませんでした。

ファクトリクラスやビルダークラスなど、オブジェクトを返すように特別に指定されたクラスは別として、メソッドがオブジェクト、クラスのプロパティの1つによって保持されているオブジェクト、またはデメテルの法則に違反するオブジェクトを返すことは問題ありませんか(1) ?

これは、デメテルに違反する機会を作り出すだけです。これは違反ではありません。これは必ずしも悪いことではありません。

そして、それがデメテルの法則に違反する場合、返されるオブジェクトがデータの一部を表し、このデータのゲッターのみを含む不変オブジェクトであるかどうかは問題になりますか?

不変は良いですが、ここでは無関係です。長いチェーンを介して物事を取得しても、それは改善されません。それを改善するのは、取得を使用から分離することです。使用している場合は、パラメーターとして必要なものを尋ねます。見知らぬ人のゲッターを掘り下げて探しに行かないでください。

擬似コードで:

デメテルの法則は、上記のようなパターンを禁じていると思います。法律に違反せずにdoSomethingElse()を呼び出せるようにするにはどうすればよいですか(3)。

私が話す前にx.doSomethingElse(a)、あなたが基本的に書いたことを理解してください

b.getA().doSomething()

現在、LoDはドットを数える練習ではありません。しかし、チェーンを作成するとき、A(を使用してB)を取得する方法を知っており、を使用する方法を知っていると言いますA。まあ今ABあなたがちょうどそれらを一緒に結合されたので、親しい友人も優れていました。

あなただけの手に何かを求めていた場合は、Aことのようにあなたが使用することができA、それはから来て、どこ世話をしなかったであろうBになって人生の幸せとあなたの強迫観念の自由を生きることができAからB

x.doSomethingElse(a)どこの詳細なしxから来た、LODはそれについて言うことは全く何の関係もありません。

LoDは、使用を構造から分離することを促進できます。しかし、すべてのオブジェクトを親しみのないものとして宗教的に扱うと、静的メソッドでコードを書くのにこだわることになります。このようにメインの素晴らしく複雑なオブジェクトグラフを構築できますが、最終的には、動作を開始するためにメソッドを呼び出す必要があります。あなたは単にあなたの友人が誰であるかを決定する必要があります。それから逃れることはできません。

そのため、クラスはLoDの下でメンバーの1つを返すことができます。その場合、そのメンバーがクラスに友好的かどうかを明確にする必要があります。そうでない場合、一部のクライアントは、使用する前にそれを取得することでそのクラスに結合しようとする場合があります。これは重要です。なぜなら、返されるものは常にその使用をサポートする必要があるからです。

多くの場合、これは単に懸念事項ではありません。コレクションは、それらを使用するすべてのものと友達になることを意図しているため、単にこれを無視できます。同様に、値オブジェクトは誰にとっても使いやすいです。しかし、住所を要求するのではなく、住所を抽出する従業員オブジェクトを要求する住所検証ユーティリティを作成した場合、従業員と住所の両方が同じライブラリからのものであることを願っています。


親しみやすさの概念のため、流FluなインターフェイスはLODの例外ではありません。流なインターフェイスでは、チェーン内の各メソッドはすべて自己を返すため、同じオブジェクトで呼び出されます。 settings.darkTheme().monospaceFont()の構文上の砂糖ですsettings.darkTheme(); settings.monospaceFont();
ジョナ

3
@Jonah自己復帰は究極の親しみやすさです。そして、すべての流fluentなインターフェイスがそうするわけではありません。多くのiDSLも流fluentであり、さまざまなタイプを返し、さまざまなポイントで利用可能なメソッドを変更します。jOOQステップビルダーウィザードビルダー、そして私自身の悪夢のようなモンスタービルダーを考えてみてください。流Fluはスタイルです。パターンではありません。
candied_orange

2

オブジェクトを返すように特別に指定されたクラスは別[...]

私はこの議論をよく理解していません。すべてのオブジェクトは、メッセージ呼び出しの結果としてオブジェクトを返すことができます。特に「オブジェクトとしてすべて」を考える場合。

ただし、クラスは、「新しい」メッセージを送信して独自の種類の新しいオブジェクトをインスタンス化できる唯一のオブジェクトです(または、ほとんどの一般的なOOP言語で新しいキーワードに置き換えられることがよくあります)。


とにかく、あなたの質問に関しては、他の場所で使用するためにオブジェクトのプロパティの1つを返すオブジェクトについてはかなり疑わしいでしょう。このオブジェクトは、その状態または実装の詳細に関する情報を漏えいさせている可能性があります。

また、CオブジェクトにBの実装の詳細を知らせたくないでしょう。これは、Cオブジェクトの観点から見たAオブジェクトへの望ましくない依存関係です。

CがAを知ることで、LODとは関係なく、彼がしなければならない仕事についてあまり知りすぎていないかどうかを自問したくなるかもしれません。

これが正当である場合があります。たとえば、コレクションオブジェクトにそのアイテムの1つを要求することが適切であり、デメテルルールに違反しない場合があります。一方、BookオブジェクトにSQLConnectionオブジェクトを要求するのは危険であり、避ける必要があります。

LODが存在するのは、多くのプログラマーが作業を実行する前にオブジェクトに情報を要求する傾向があるためです。LODは、(常にではないにしても)時として、オブジェクトにあまりにも多くのことをさせて、物事を過度に複雑にしていることを思い出させてくれます。別のオブジェクトに作業を依頼する必要がある場合があります。LODは、オブジェクトのどこにビヘイビアーを配置するかを再考するためのものです。


0

ファクトリクラスやビルダークラスなど、オブジェクトを返すように特別に指定されたクラスは別として、メソッドがオブジェクトを返すことは問題ありませんか?

なぜそうではないのですか?クラスがオブジェクトを返すことを特に意図していること、またはオブジェクトをまったく返してはならないことを仲間のプログラマに伝える必要があることを示唆しているようです。すべてがオブジェクトである言語では、何も返せないため、これによりオプションが大幅に制限されます。


この例は、暗黙的b.getA().doSomething()およびx.doSomethingElse(b.getA())
user2180613

A a = b.getA(); a.DoSomething();-1つのドットに戻ります。
ロバートハーベイ

2
@ user2180613-質問したい場合は、明示的に質問する必要がb.getA().doSomething()ありx.doSomethingElse(b.getA())ます。現状では、あなたの質問はについてのように見えますthis.b.getA()
デビッドハンメン

1
Aメンバーではないので、でC作成Cも提供もされていないためCAinをC(何らかの方法で)使用するとLoDに違反するようです。ドットのカウントはLoDの目的ではなく、単なる例示ツールです。Driver().getCar().getSteeringWheel().getFrontWheels().toRight()それぞれが1つのドットを含む個別のステートメントに変換することは可能ですが、LoDの違反は依然として存在します。このフォーラムの多くの投稿で、アクションの実行は実際の動作を実装するオブジェクトに委任されるべきであると読みましたtell don't ask。値を返すことはそれと矛盾するようです。
user2180613

1
私はそれが本当だと思うが、それは質問に答えていない。
user2180613

0

何をしているのかに依存します。クラスのメンバーを公開する場合、それがあなたのクラスのメンバーであること、またはそのメンバーのタイプが誰のビジネスでもない場合、それは悪いことです。その後、そのメンバーの型、およびメンバーを返す関数の型を変更し、全員がコードを変更する必要がある場合、それは悪です。

一方、クラスのインターフェイスで、既知のクラスAのインスタンスを提供するように指定されている場合は、問題ありません。運が良ければ、クラスのメンバーを提供することでこれを行うことができます。そのメンバーをAからBに変更した場合、Aを返すメソッドを書き直す必要があります。明らかに、1ライナーを返すだけでなく、1を構築して利用可能なデータを入力する必要があります。メンバー。クラスのインターフェースは変更されません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.