4

C#、MVC4、StructureMap などを使用した Web ソリューションで作業しています。

ソリューションには、コントローラー用のサービスがあります。例によって:

public class ServiceA{
    private readonly IRepository _repository1;
    private readonly IRepository _repository2;

    public ServiceA(IRepository1 repository1, IRepository2 repository2){
        _repository1=repository1;
        _repository2=repository2;
    }

    public void DoSomethingA(){
        _repository1.DoSomething();
    }

    public void DoSomethingB(){
        _repository2.DoSomething();
    }
}

public class ServiceB{
    private readonly IRepository _repository3;
    private readonly IRepository _repository4;

    public ServiceB(IRepository3 repository3, IRepository4 repository4){
        _repository3=repository3;
        _repository4=repository4;
    }

    public void DoSomethingA(){
        _repository3.DoSomething();
    }

    public void DoSomethingB(){
        _repository4.DoSomething();
    }
}

これを行うのは良い習慣ですか?:

public abstract class ServiceBase(){
    public IRepository1 Repository1 { get { return instanceOf<IRepository1>(); }}
    public IRepository2 Repository2 { get { return instanceOf<IRepository2>(); }}
    public IRepository3 Repository3 { get { return instanceOf<IRepository3>(); }}
    public IRepository4 Repository4 { get { return instanceOf<IRepository4>(); }}

    private T instanceOf<T>()
    {
        return ServiceLocator.Current.GetInstance<T>();
    }
}

そして、この方法でサービスを作成しますか?

public class ServiceA : ServiceBase
{
    public void DoSomethingA(){
        Repository1.DoSomething();
    }

    public void DoSomethingB(){
        Repository2.DoSomething();
    }
}


public class ServiceB : ServiceBase
{
    public void DoSomethingA(){
        Repository3.DoSomething();
    }
    public void DoSomethingB(){
        Repository4.DoSomething();
    }
}

2番目のオプションには、特定の利点があります。

  • リポジトリごとにプライベート変数を持つ必要はありません。
  • サービスのコンストラクターは必要ないので、サービスを小さくして読みやすくします。
  • すべてのリポジトリは、どのサービスでも利用できます。
  • サービスは不要なインスタンスを取得しません。たとえば、get onlyインスタンスServiceAのメソッドを呼び出します。(最初のメソッドを使用すると、 forとの 2 つのインスタンスを受け取ります)DoSomethingAServiceLocatorRepository1Repository1Repository2

どちらの場合も、適切なテストを行うことができます。

  • 最初のケースでは、モック化されたオブジェクトをコンストラクターを介して送信します。
  • 2 番目のケースでは、必要に応じてモック オブジェクトを使用するように StructureMap を構成します。

と思いますか?私はいくつかの原則に反していますか?(私の英語でごめんなさい)

4

2 に答える 2

8

最初に利点の引数を見てみましょう。

リポジトリごとにプライベート変数を持つ必要はありません。

そのとおりです。実際には、参照用のこれらの 4 バイトは通常問題になりません。

サービスのコンストラクターは必要ないので、サービスを小さくして読みやすくします。

私はそれをまったく逆に見ています。コンストラクターがあると、クラスにどのような依存関係があるかがすぐにわかります。基本クラスでは、クラス全体を調べてその情報を取得する必要があります。また、カップリングが低く、凝集度が高く、もつれのない優れた設計があるかどうかを分析するツールを使用することもできません。そして、クラスを使用している人は、実装を読まない限り、クラスにどのような依存関係があるのか​​ まったくわかりません。

すべてのリポジトリは、どのサービスでも利用できます。

結合が増えるため、1 つのサービスで多くのリポジトリを使用することは避ける必要があります。すべてのサービスにすべてのリポジトリを提供することは、結合度の高い不適切な設計を助長する最善の方法です。だからこれはデメリットだと思います。

サービスは不要なインスタンスを取得しません。たとえば、ServiceA でメソッド DoSomethingA を呼び出すと、ServiceLocator は Repository1 インスタンスのみを取得します。(最初のメソッドを使用すると、Repository1 と Repository2 の 2 つのインスタンスを受け取ります)

完全に異なる依存関係と異なる方法を使用するサービスは、単一責任の原則に従っていないことを示す大きな兆候です。この場合、おそらく 2 つのサービスに分割する必要があります。

テスト容易性について: シングルトン (ServiceLocator) を使用する 2 番目のケースでは、テストはもはや分離されません。したがって、それらは互いに影響を与えることができます。特に並行して実行する場合。

私の意見では、あなたは間違った道を進んでいます。Service Locator アンチパターンを使用すると、クラスを使用する人に依存関係が隠され、実装を読む人がクラスの依存関係を確認しにくくなり、テストが分離されなくなります。

于 2012-08-05T01:03:18.570 に答える
1

この設計で私が目にする問題は、ServiceLocator をサービスに密結合しているため、疎結合を促進していないことです。モック/テスト リポジトリを使用してサービスを単体テストしたい場合はどうなりますか? または、DI 実装を交換することもできます。実際には、サービス ロケーターを介してテスト サービスを構成できるため、おそらく大きな問題にはなりませんが、コードの臭いがするだけです。

于 2012-08-05T00:06:48.323 に答える