あるオブジェクト(A)からあるオブジェクト(B)を参照するだけで、依存が発生する。

以下の例では、UserApplicationServiceが特定の技術基盤に依存している問題がある。この場合はIUserRepositoryといった抽象に依存すべきである。

public class UserApplicationService
{
  private UserRepository userRepository;
}
 
// [UserApplication] --> [UserRepository]

依存関係逆転の原則

  • 上位レベルのモジュールは下位レベルのモジュールに依存してはならない。どちらのモジュールも抽象に依存すべきである。
    • UserApplicationService --> IUserRepository
    • UserRepository --o IUserRepository
  • 抽象は、実装の詳細に依存してはならない。実装の詳細が抽象に依存すべきである。
    • 高レベルなモジュール(UserApplicationService)を主体に、インターフェイス(IUserRepository)を宣言する。
      • 実装の詳細は、宣言されたインターフェイスに合わせる

依存関係のコントロール

Service Locatorパターン

事前にServiceLocatorオブジェクトに依存関係の解決先を登録しておき、必要になったらインスタンスを取得するパターン

ServiceLocator.Register<IUserRepository, InMemoryUserRepository>();
public class UserApplicationService
{
  private readonly IUserRepository userRepository;
 
  public UserApplicationService()
  {
    // ServiceLocator経由でインスタンスを取得する
    this.userRepository = ServiceLocator.Resolve<IUserRepository>();
  }
}
  • Pros
    • 導入しやすい
  • Cons
    • 依存関係が外部から見えづらい
      • コンストラクタで依存関係を解決しているため、クライアントは「事前にServiceLocatorの設定が必要」だということがわかりづらい
    • テストの維持が難しい
      • 依存関係を追加すると、コンストラクタでエラーが発生するためテストが壊れる

IoC Containerパターン

コンストラクタでDIできるようにし、依存するオブジェクトのインスタンス化をIoC Containerで行う

public class UserApplicationService
{
  private readonly IUserRepository userRepository
 
  public UserApplicationService(IUserRepository userRepository) {
   this.userRepository = userRepository
  }
}