あるオブジェクト(A)からあるオブジェクト(B)を参照するだけで、依存が発生する。
以下の例では、UserApplicationServiceが特定の技術基盤に依存している問題がある。この場合はIUserRepositoryといった抽象に依存すべきである。
public class UserApplicationService
{
private UserRepository userRepository;
}
// [UserApplication] --> [UserRepository]依存関係逆転の原則
- 上位レベルのモジュールは下位レベルのモジュールに依存してはならない。どちらのモジュールも抽象に依存すべきである。
UserApplicationService --> IUserRepositoryUserRepository --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
}
}