Dependency Injection(DI)は有益だという前提で話をします
「SOLID原則」などに代表されるオブジェクト指向設計のパターンや原則は、「まあ、大事だよねえ」と思いつつも、「そうは言っても今一つピンと来ないなあ」と思ったりすることもあったりします。
ですので、今日はDIコンテナの話を書くのですが、「なぜ有益か」という話はあまり深入りしないようにしようと思います。
DIは結合度を減らす
例えば、下記のようなコードを考えます。
public class NiceController : Controller { public static string Name { get; } = "Nice"; private readonly ValueService _service; public NiceController() { _service = new ValueService(); } public IActionResult Index() { ViewData["value"] = _service.GetValue(); return View(); } }
// Services\ValueService.cs public class ValueService { public int GetValue() { return this.GetHashCode(); } }
このコードは、コントローラーのコンストラクタでサービスであるValueServiceインスタンスを生成し、Indexアクションにてそれを利用しています。
こういうコードはよくありがちですが、コントローラーとサービスのクラスが強く「結合」しており、一般的によろしくないとされています。
この場合のサービスValueServiceを「依存しているオブジェクト」("dependency")と呼びます。
では次にこういう結合を弱めるためにできることとして、外部からインスタンスを与えます。
private readonly ValueService _service; public NiceController(ValueService service) { _service = service; }
上記のようにコンストラクタに対して、インスタンスを外部から与えることができれば、コントローラー側はサービスValueServiceのインスタンス生成について考慮する必要がなくなります。
さらにいうとコントローラーはサービスの処分についても考慮する必要がなくなるでしょう。
上記のように外部から依存しているオブジェクトを与えることがDependency Injection(DI)パターンと呼ばれるものです。
さらに一歩進める
上記でDIは一応成り立っているのですが、さらに一歩前進して、下記のように抽象化します。
private readonly IValueService _service; public NiceController(IValueService service) { _service = service; }
// Services\IValueService.cs public interface IValueService { int GetValue(); }
// Services\ValueService.cs public class ValueService : IValueService { public int GetValue() { return this.GetHashCode(); } }
上記により、コントローラー側はサービスの実装(ValueService)との結合がなくなり、IValueService*1を実装したサービスであれば受け取れるようになりました。
これにより、
- サービスの変更が容易になる
- ユニットテストが簡単になる
などのメリットがあります。
それでは、どうやってコントローラーのコンストラクタに ValueService(=ValueService) を渡すのでしょうか。
そのための仕組みがDIコンテナです。
ASP.NET Core MVCでは組み込みのDIコンテナが実装され、簡単に使うことができます。
その解説は次回にします。
おすすめリンク
*1:Interfaceの抽出はVisualStudioの右クリックメニュー[Quick Actions and Refactorings...]を使うと便利です