2016-10-07 10 views
0

依存性注入法、デメタ法とグローバル状態(シングルトンはグローバルとみなされます)について2つの素晴らしいビデオ(thisthis)を見ました。依存関係注入法+デメテルの法則+ロガー/アサート

私は基本的な考え方があると思いますが、私のライブラリにはすでにシングルトンクラスがいくつかあります。しかし、私がテスト可能で「うまく設計された」コードや「あまり結合されていない」コードが必要な場合は、DIとLoDを使用する必要があります。これはもちろん、呼び出し側が実装ではなく、少なくともテストの観点からはグローバルなものへの依存性が悪いため、(設計パターンとしての)シングルトンは悪いことです。

もっと具体的には、サードパーティの大きなライブラリを使用せずに単純なゲームエンジンを構築しています。つまり、プラットフォーム固有の低レベルのコードでも作業する必要があります。

もっと具体的にしましょう。私はライブラリのVector2クラスを持っている数学のセクションを持っています。その機能の1つに対して無効なデータが入力された場合、「アサートをスローする」ことができます。または、エラーとしてログに記録することができます。または両方。今まではSingleton<Logger>を使用していたので、どこにでもアクセスできました。

しかし、私はこれらのことを使用すべきではなく、DIがこれらの問題を解決することに同意します。例えば。ロガーがまだ初期化されていない場合はどうなりますか?テスト用のダミーロガーが必要な場合はどうすればよいですか?そういうわけで...(LoggerクラスやAssertクラスのような)これらのケースでは何をお勧めしますか?

また、LoDにはオブジェクトのアクセサ(getObjectA()->getObjectB()->doSomething()など)を使用しないでください。代わりに、それらを関数/コンストラクタのパラメータとして渡します。すべてのテスト(デバッグ)を簡単にすることはできますが、これらの機能をスキップするのは苦労かもしれません。

Unityエンジンの例を考えてみましょう。 GameObjectには、そのオブジェクトからコンポーネントを取得するメソッドがあります。例えば。私は手動で私のオブジェクトを変換したい場合、私は、「オブジェクトゲッター」、このような何かを呼び出すために選択の余地がない:

this.GetComponent<Transform>().SetPosition(...); 

これはのLoDに反しているが、それはないですか?

答えて

0

これは、プラットフォーム固有の低レベルコードでも処理する必要があることを意味します。

dependency inversion(注射だけでなく)を使用する。

(LoggerクラスとAssertクラスのような)これらのケースでは、どのようなことをお勧めしますか?

DIでは、使用する場所に物事を注入できるようにAPIを変更する必要があります。あなたは(ようにロガーの1、アサート実装のための1つ、またはグローバルコンフィギュレーション設定と)余分なパラメータの多くを追加する必要があり、状況、一緒にグループにそれらを避けるために:

  • は、実行時の設定クラスを作成
  • それにサービスを追加する(ロガーサービス、検証サービス、設定サービスなど)
  • 実行時設定を渡します。

またLODが、私は(getObjectAのように() - > getObjectB() - > doSomethingの())オブジェクトに対してアクセサを使用してはならないと言っています。代わりに、それらを関数/コンストラクタのパラメータとして渡します。

コールチェーンのこのタイプのいくつかの問題があります。

  • が、それは繰り返しを促す(あなたのコードでgetObjectA()->getObjectB()->何回も持っている場合、それはすでにメンテナンスの問題である)

  • これは不完全な設計の貧弱な代用品です。

    void ObjectA::doSomething(ObjectA& a) 
    { 
        getObjectB()->doSomething(); 
    } 
    

    (または類似):LODはあなたがObjectAのインスタンスから始まるdoSomething()する必要がある場合は、ObjectAには、メソッドのdoSomethingを持つべきであると述べています。

    メンテナンスに適した「ObjectAのインスタンスから始まるdoSomethingがどのように行われるか」を拡張するための自然なポイントが追加されています。

  • これは、doSomethingに必要なすべてのクライアントコードに、ObjectBのインターフェイスについて知る必要があるという事実を課すものです。これは小さく聞こえますが、問題は広がっています。デザインポリシーとして適用すると、ObjectBがObjectBだけでなくObjectCとObjectDを持っていれば、これはかなり複雑になります依存関係を維持するだけの時間)。

this.GetComponent<Transform>().SetPosition(...);

これはのLoDに反している、それはないですか?

はい。

void SetPosition(Transform& t) { t.SetPosition(); } 

クライアントコード:

SetPosition(this.GetComponent<Transform>()); 

この方法では、クライアントコード内の位置を設定するために、あなたはもはやトランスフォームのインタフェースを気にしないコードは、次のように分割することができます。 void SetPosition(Transform& t)の実装には気にしません。GetComponentというAPIがあります。上記のご例えば

代替のLOD実装:

void YourObject::SetTransformPositions() 
{ 
    GetComponent<Transformation>.SetPosition(); 
} 

... thisのタイプはYourObject*です。

+0

ありがとうございました。私は既にあなたが上記の "コレクションクラス"を作成しました。また、私は十分に具体的ではありませんでした。もちろん、低レベルのプラットフォームコードを直接使用するのではなく、代わりに抽象クラス/インタフェースを使用しています。 – csisy