オブジェクトがその所有者の多くを知っている場合、それはコードのにおいですか?


9

Delphi 2007アプリケーションでは、次の構成要素の多くを使用しています

FdmBasic:=TdmBasicData(FindOwnerClass(AOwner,TdmBasicData));

FindOwnerClassは、現在のコンポーネントの所有者階層を上に移動して、特定のクラスを検索します(例ではTdmBasicData)。結果のオブジェクトは、フィールド変数FdmBasicに格納されます。これは主にデータモジュールを渡すために使用します。

例:レポートを生成するとき、結果のデータは圧縮され、データモジュールTdmReportBaseDataを介してアクセスされるテーブルのBlobフィールドに格納されます。アプリケーションの別のモジュールには、ReportBuilderを使用して、レポートのデータをページ形式で表示する機能があります。このモジュールのメインコード(TdmRBReport)は、クラスTRBTempdatabaseを使用して、圧縮されたblobデータを、Reportbuilderランタイムレポートデザイナーで使用可能なさまざまなテーブルに変換します。TdmRBReportは、あらゆる種類のレポート関連データ(レポートのタイプ、レポート計算設定など)のTdmReportBaseDataにアクセスできます。TRBTempDatabaseはTdmRBReportで構築されますが、TdmReportBasedataにアクセスできる必要があります。したがって、これは上記の構成を使用して行われます。

constructor TRBTempDatabase.Create(aOwner: TComponent);
begin
  inherited Create(aOwner);

  FdmReportBaseData := TdmRBReport(FindOwnerClass(Owner, TdmRBReport)).dmReportBaseData;
end;{- .Create }

私の考えでは、これはTRBTempDatabaseがその所有者の多くを知っていることを意味し、これがなんらかのコードの匂いやアンチパターンなのかどうか疑問に思っていました。

これについてどう思いますか?これはコードのにおいですか?もしそうなら、より良い方法は何ですか?


1
別のクラスについて多くのことを知っているとすれば、それを行うためのより簡単な方法が提供されただろう。
Loren Pechtel

回答:


13

この種類は、(一般的なアンチパターンとして識別されている)Martin Fowlerによって最初に説明されたService Locatorパターンに似ています。

構造ベースの依存性注入は、必要なパラメーターの可視性を促進し、より簡単な単体テストを促進するため、サービスロケーターよりも推奨されます。

Service Locatorを使用する際の問題は、特定のService Locator実装に依存することではありません(ただし、それも問題になる可能性があります)が、それは正真正銘のアンチパターンです。APIの利用者に恐ろしい開発者エクスペリエンスを提供します。また、行うすべての変更の影響を把握するためにかなりの量の頭脳力を使用する必要があるため、メンテナンス開発者としての生活を悪化させます。

コンストラクターインジェクションを使用すると、コンパイラーはコンシューマーとプロデューサーの両方に多くの支援を提供できますが、Service Locatorに依存するAPIにはその支援はありません。

また、最も顕著なのは、デメテルの法則も破る

デメテルの法則(LoD)または最小知識の原則は、ソフトウェア、特にオブジェクト指向プログラムを開発するための設計ガイドラインです。一般的な形式では、LoDは疎結合の特定のケースです。

関数のデメテルの法則では、オブジェクトOのメソッドMは、次の種類のオブジェクトのメソッドのみを呼び出すことができる必要があります。

  1. O自体
  2. Mのパラメータ
  3. M内で作成/インスタンス化されたオブジェクト
  4. Oの直接コンポーネントオブジェクト
  5. Mのスコープ内でOからアクセス可能なグローバル変数

特に、オブジェクトは、別のメソッドによって返されたメンバーオブジェクトのメソッドを呼び出さないようにする必要があります。フィールド識別子としてドットを使用する多くの現代のオブジェクト指向言語では、法律は単に「1つのドットのみを使用する」と表現できます。つまり、コードabMethod()は、a.Method()が違反しない法則に違反します。簡単な例として、犬の散歩をしたい場合、犬の足に直接歩くように命令するのは愚かです。代わりに、犬を指揮し、自分の足の世話をさせます。

より良い方法

効果的には、クラス内のサービスロケーターコールを削除し、コンストラクター内のパラメーターとして正しい所有者を渡すのがより良い方法です。これが所有者のルックアップを実行し、それをクラスコンストラクターに渡すサービスクラスがあることを意味する場合でも

constructor TRBTempDatabase.Create(aOwner: TComponent, ownerClass: IComponent);
begin
  inherited Create(aOwner);

  FdmReportBaseData := TdmRBReport(ownerClass).dmReportBaseData;
end;{- .Create }

3
すばらしい答え、そしてこの素晴らしいアナロジーを思いついた人への小道具:As a simple example, when one wants to walk a dog, it would be folly to command the dog's legs to walk directly; instead one commands the dog and lets it take care of its own legs.
アンディ・ハント

3

子オブジェクトに親についての知識が多すぎる場合の難しさの1つは、結局、結合が強すぎて(ほとんどの場合そうなります)、過度に依存する頭痛を引き起こし、後で安全に変更および維持することが非常に困難になるパターンを実装してしまうことです。後で。

2つのクラスがどの程度深く接続されているかによって、FowlerがFeature EnvyまたはInappropriate Intimacyのコードの匂いを説明しているように見えます。

データを含むクラスをロードまたは読み取る必要があるようです。その場合、いくつかの代替パターンを使用して、子とその親のチェーンとの間の依存関係を壊すことができ、アクセスのタスクを委任する必要があるようですデータアクセサクラスがすべての処理を実行するのではなく、データクラス。

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