C#でAというクラスを作成するとき、その内部で別のBというクラスのインスタンスを生成して利用することがあります。
この時クラスAはクラスBに依存していると言えますが、このような依存関係が強くなると面倒なことが増えてきます。依存関係を薄くするために、クラスBはクラスAの中でインスタンスを生成するのではなく外部で生成して注入するように作ろうというデザインパターンです。
依存性が強いことで起こる不都合
以下のようなクラスを例にしてみます。
public class A { private B TestB; public A() { TestB = new B(); } public void Echo() { System.Console.WriteLine(TestB.GetMessage()); } } public class B { public string GetMessage() { return "クラスBです"; } } public class Program { public void Main(){ var testA = new A(); testA.Echo(); } }
単体テストが大変
上記の例で、クラスAをテストしようとした場合、クラスBが完成している必要があります。
大きなプロジェクトになればそれぞれを別の担当者が作ることもあり、クラスBが完成しないからテストが進められないという事になりかねません。
テスト用の仮クラスB(テストダブル)を作ってテストする方法もありますが、DIの手法を用いればより簡単にテストクラスを準備しテストできます。
コードの再利用性が低い
クラスBを少し改良したクラスB2を作成したとしましょう。
クラスAは内部でクラスBのインスタンスを生成しているのでクラスB2は利用できません。
クラスAを改良してクラスB2に対応させるか、クラスB2用のクラスA2を作成するか・・・どちらも大変面倒な作業です。
DIデザインパターンでコードを書く
上記の例をDIの考え方を用いて書き換えてみます。
public class A { private B TestB; public A(B b) { TestB = b; } public void Echo() { System.Console.WriteLine(TestB.GetMessage()); } } public interface B { public string GetMessage(); } public class B1 : B { public string GetMessage() { return "クラスB1です"; } } public class B2 : B { public string GetMessage() { return "クラスB2です"; } } public class Program { public void Main(){ var testB = new B1(); var testA = new A(testB); testA.Echo(); } }
クラスBのかわりにインターフェースBを作りました。
クラスAの内部でクラスBを new するのではなく、インターフェースBを持ったオブジェクトを外部から注入する仕組みになっています。
インターフェースを間に挟むことでクラスAは特定のクラスに依存することなく機能することができるようになります。クラスB1をクラスB2に差し替えることも容易になります。
DIコンテナというフレームワーク
DIはとても有益な考え方ですが、依存関係が複雑になるような大きなシステムでは、オブジェクトを生成する部分のコードが複雑になってしまいます。
オブジェクトの生成を一元管理してDIを使いやすくしてくれるフレームワークがDIコンテナです。
.Net Core 環境では「Microsoft.Extensions.DependencyInjection」というDIコンテナが提供されています。
コメントをお書きください