コンポーネントは、注入された依存関係を破棄してはなりません。その主な理由は次のとおりです。
- そのコンポーネントはそれらを作成しなかったため、それらの依存関係を破棄する必要があるかどうかわかりません。
- 消費者は、依存関係が破棄可能であることを意識するべきではありません。
コンポーネントがより長いライフタイムを持つサービスに依存することは非常に一般的です。消費するコンポーネントがその依存関係を破棄する場合、使用するように構成されている間は依存関係を使用できなくなるため、アプリケーションは壊れます。簡単な例を次に示します。
// Singleton
private static readonly IRepository<User> repository = new UserRepository();
public IController CreateController(Type controllerType) {
if (controllerType == typeof(UserController)) {
return new UserController(repository);
}
// ...
}
この例には、 singletonUserRepository
と transientが含まれていUserController
ます。要求ごとに、新しいUserController
ものが作成されます (ASP.NET MVC アプリケーションを想像してみてください。これは理にかなっています)。がUserController
を破棄するUserRepository
と、次のリクエストはUserController
、既に破棄された に依存する を取得しUserRepository
ます。これは明らかに悪いでしょう。
しかし、これ以外に実装IRepository<T>
すべきではありませんIDisposable
。実装IDisposable
とは、抽象化が実装の詳細を漏らしていることを意味し、そのため、次のように述べられているDependency Inversion Principleに違反しています。
抽象化は詳細に依存すべきではありません。詳細は抽象化に依存する必要があります。
抽象化での実装は、その抽象化のすべての実装がそれ自体を破棄する必要があることIDisposable
を完全に 100% 確信している場合にのみ意味があります。しかし、これはめったにありません。単体テストに実装があることを想像してみてください。このような偽の実装は破棄を必要としないため、すべての実装が破棄を必要とするわけではなく、実装の詳細を漏らしています。FakeRepository<T>
これは単に、IDisposable
インターフェイスを実装に移動する必要があることを意味します。例えば:
public interface IRepository<T> { }
public class UserRepository : IRepository<User>, IDisposable { }
IDisposable
すべてのコンシューマーが呼び出すとは限らないが、抽象化されたインターフェイスを持つことは、次のように述べているインターフェイス分離の原則Dispose
に違反していることにも注意してください。
クライアントは、使用しないメソッドに依存することを強制されるべきではありません。
これの利点は、コンシューム コンポーネント ( などUserController
) が誤って呼び出さDispose()
れ、システムが破損する可能性がなくなることです。
もう 1 つの利点は、コンポーネントが依存関係を破棄する必要がないため、ほとんどのコンポーネントで破棄ロジックが残らず、システムが大幅に単純になり、保守しやすくなることです。