37

同僚と私は顧客のためにシステムを設計しました。私たちの意見では、きれいなデザインを作成しました。しかし、導入したいくつかのカップリングに問題があります。私たちの設計と同じ問題を含むサンプル デザインを作成することもできますが、ご容赦いただければ、質問をサポートするために私たちの設計の抜粋を作成します。

患者の特定の治療を登録するためのシステムを開発しています。画像へのリンクが壊れないように、概念的な UML クラス図を ac# スタイルのクラス定義として説明します。

class Discipline {}
class ProtocolKind 
{ 
   Discipline; 
}
class Protocol
{
   ProtocolKind;
   ProtocolMedication; //1..*
}
class ProtocolMedication
{
   Medicine;
}
class Medicine
{
   AdministrationRoute;
}
class AdministrationRoute {}

設計について少し説明しようと思います。プロトコルは新しい治療のテンプレートです。そして、プロトコルは特定の種類のものであり、投与する必要のある薬が含まれています。プロトコルごとに、同じ薬の投与量が (とりわけ) 異なる可能性があるため、これは ProtocolMedication クラスに格納されます。AdministrationRoute は、薬が投与される方法であり、プロトコル管理とは別に作成/更新されます。

デメテルの法則に違反する可能性のある次の場所を発見しました。

デメテルの法則の違反

BLLの内部

たとえば、ProtocolMedication のビジネス ロジック内には、医薬品の AdministrationRoute.Soluble プロパティに依存するルールがあります。コードは次のようになります

if (!Medicine.AdministrationRoute.Soluble)
{
   //validate constrains on fields
}

リポジトリ内

特定の分野のすべてのプロトコルをリストするメソッドは、次のように記述されます。

public IQueryable<Protocol> ListQueryable(Discipline discipline)
{
    return ListQueryable().Where(p => (p.Kind.Discipline.Id == discipline.Id)); // Entity Frameworks needs you to compare the Id...
}

ユーザー インターフェイスの内部

システムのインターフェースに ASP.NET (MVC なし) を使用していますが、私の意見では、このレイヤーには現在最悪の違反があります。グリッドビューのデータバインディング (プロトコルの規律を表示する必要がある列は、Kind.Discipline.Name にバインドする必要があります) は文字列であるため、コンパイル時のエラーは発生しません

<asp:TemplateField HeaderText="Discipline" SortExpression="Kind.Discipline.Name">
   <ItemTemplate>
      <%# Eval("Kind.Discipline.Name")%>
   </ItemTemplate>
</asp:TemplateField>

ですから、実際の問題は、いつそれをデメテルの暗示と見なすのがよいのでしょうか、そしてデメテルの法則の違反を解決するために何ができるでしょうか?

私は自分自身についていくつかのアイデアを持っていますが、それらを回答として投稿して、個別にコメントしたり投票したりできるようにします. (そうでない場合は、回答を削除して質問に追加します)。

4

10 に答える 10

21

私は全滅に反対票を投じられることを知っていますが、私はデメテルの法則が嫌いだと言わなければなりません. 確かに、次のようなもの

dictionary["somekey"].headers[1].references[2]

本当に醜いですが、これを考慮してください:

Kitchen.Ceiling.Colour

私はこれに反対するものは何もありません。このようにデメテルの法則を満たすためだけにたくさんの関数を書く

Kitchen.GetCeilingColour()

私にとっては時間の無駄のように見えますが、実際には物事を成し遂げるための私の方法です。たとえば、要件が変更され、天井の高さも必要になった場合はどうなりますか? Demeter の法則では、Ceiling の高さを直接取得できるように Kitchen に別の関数を記述する必要があります。

編集:私のポイントを言い換えましょう:

物事を抽象化するこのレベルは非常に重要なので、3、4、5 レベルのゲッター/セッターを書くのに時間を費やす必要があるのでしょうか? 本当にメンテナンスが楽になるの?エンドユーザーは何かを得ますか? 私の時間の価値はありますか?私はそうは思わない。

于 2009-01-22T10:47:58.437 に答える
12

デメテル違反に対する従来の解決策は、「聞くな、教えろ」です。つまり、状態に基づいて、管理対象オブジェクト (保持しているオブジェクト) に何らかのアクションを実行するように指示する必要があります。すると、状態に応じて、要求したことを実行するかどうかが決定されます。

簡単な例として、私のコードはロギング フレームワークを使用しており、ロガーにデバッグ メッセージを出力したいと伝えています。次に、ロガーはその構成 (おそらくデバッグが有効になっていない) に基づいて、実際にメッセージを出力デバイスに送信するかどうかを決定します。この場合の LoD 違反は、オブジェクトがメッセージに対して何かを行うかどうかをロガーに尋ねることです。そうすることで、コードをロガーの内部状態の知識に結び付けることができました (そうです、意図的にこの例を選びました)。

ただし、この例の重要な点は、ロガーがビヘイビアーを実装することです。

LoD が壊れると私が思うのは、データを表すオブジェクトを扱うときであり、動作はありません。

その場合、オブジェクト グラフをトラバースする IMO は、XPath 式を DOM に適用するのと同じです。また、「isThisMedicationWarranted()」などのメソッドを追加するのは、より悪いアプローチです。オブジェクト間でビジネス ルールを配布しているため、理解が難しくなります。

于 2009-01-22T13:57:57.883 に答える
4

The Clean Code Talks というセッションを見るまでは、多くの皆さんと同じように LoD に苦労していました。

「ものを探すな」

このビデオは、本質的に LoD の問題を解決できる Dependency Injection をより適切に使用するのに役立ちます。設計を少し変更することで、親オブジェクトを構築するときに多くの下位レベルのオブジェクトまたはサブタイプを渡すことができるため、親が子オブジェクトを介して依存チェーンをたどる必要がなくなります。

あなたの例では、AdministrationRoute を ProtocolMedication のコンストラクターに渡す必要があります。理にかなっているように、いくつかのことを再設計する必要がありますが、それがアイデアです。

そうは言っても、LoD に不慣れで専門家ではないので、私はあなたと DrJokepu に同意する傾向があります。ほとんどのルールと同様に、LoD にはおそらく例外があり、それが設計に適用されない場合があります。

[数年遅れているため、この回答はおそらく作成者の役に立たないことはわかっていますが、それが私がこれを投稿している理由ではありません]

于 2012-07-16T23:44:54.653 に答える
2

Soluble を必要とするビジネス ロジックには、他のものも必要であると想定する必要があります。もしそうなら、その一部を意味のある方法で(Medicine.isSoluble()よりも意味のある方法で)Medicineにカプセル化できますか?

もう 1 つの可能性 (おそらくやり過ぎであり、同時に完全な解決策ではありません) は、ビジネス ルールを独自のオブジェクトとして提示し、2 つのディスパッチ/ビジター パターンを使用することです。

RuleCompilator
{
  lookAt(Protocol);
  lookAt(Medicine);
  lookAt(AdminstrationProcedure) 
}

MyComplexRuleCompilator : RuleCompilator
{
  lookaAt(Protocol)
  lookAt(AdminstrationProcedure)
}

Medicine
{
  applyRuleCompilator(RuleCompilator c) {
    c.lookAt(this);
    AdministrationProtocol.applyRuleCompilator(c);
  }
}
于 2009-01-22T12:57:08.457 に答える
1

含まれているすべてのオブジェクトのすべてのメンバーにゲッター/セッターを提供する代わりに、将来の変更にある程度の柔軟性を提供する簡単な変更の 1 つは、含まれているオブジェクトを返すオブジェクト メソッドを代わりに提供することです。

たとえば、C++ の場合:

class Medicine {
public:
    AdministrationRoute()& getAdministrationRoute() const { return _adminRoute; }

private:
    AdministrationRoute _adminRoute;
};

それで

if (Medicine.AdministrationRoute.Soluble) ...

になる

if (Medicine.getAdministrationRoute().Soluble) ...

これにより、必要に応じて DB テーブルから AdministrationRoute をフェッチするなど、後で getAdministrationRoute() を柔軟に変更できます。

于 2009-01-22T11:27:52.427 に答える
1

3 番目の問題は非常に単純です:プロパティDiscipline.ToString()を評価する必要がありNameます。Kind.Discipline

于 2013-10-18T22:03:54.400 に答える
1

BLL の場合、私の考えは、次のように Medicine にプロパティを追加することでした。

public Boolean IsSoluble
{
    get { return AdministrationRoute.Soluble; } 
}

これは、デメテルの法則に関する記事で説明されていると思います。しかし、これはクラスをどれだけ混乱させるでしょうか?

于 2009-01-22T10:18:17.653 に答える
1

「可溶性」プロパティを使用した最初の例に関して、いくつかの注意点があります。

  1. 「AdministrationRoute」とは何ですか? また、開発者はなぜそこから薬の可溶性プロパティを取得することを期待するのでしょうか? 2 つの概念はまったく無関係に見えます。これは、コードの通信がうまくいかないことを意味し、おそらく既に持っているクラスの分解が改善される可能性があります。分解を変更すると、問題に対する他の解決策が見つかる可能性があります。
  2. 可溶性は、何らかの理由で医学の直接のメンバーではありません. 直接アクセスする必要がある場合は、おそらく直接メンバーにする必要があります。追加の抽象化が必要な場合は、その追加の抽象化を薬から (直接またはプロキシまたはファサードによって) 返します。可溶性プロパティを必要とするものはすべて抽象化で機能し、基質やビタミンなどの複数の追加タイプに同じ抽象化を使用できます。
于 2009-01-22T11:03:38.263 に答える