Dependency Injectionとは、「あるオブジェクトが別のオブジェクトに依存している場合、その依存関係を外部から注入することによってオブジェクト間の結合度を低くし、柔軟性を持たせる」という考え方です。
具体的には、あるクラスAが別のクラスBに依存している場合などが考えられます。
「クラスAのコンストラクタ」で「クラスBのインスタンスを生成している」 ような場合、
クラスAとクラスBが強く結合し、クラスBを変更する場合にクラスAも変更する必要が生じてしまいます。
これを回避するために、 「クラスAのコンストラクタにクラスBのインスタンスを渡すようにする」 ことで、クラスAはクラスBに依存していることを表明し、外部からクラスBのインスタンスを注入することができるようになります。
下記にクラスAとクラスBが依存し合っている実装とDIを用いた実装を書いてみます。
クラスAがクラスBに依存している例
class ClassB {
public log(): void {
console.log('Hello, World!');
}
}
class ClassA {
private b: ClassB;
constructor() {
// クラスBをインスタンス化
this.b = new ClassB();
}
public callB(): void {
// クラスBのlogメソッドを使用
this.b.log();
}
}
// クラスAをインスタンス化
const a = new ClassA();
// クラスAのcallB()をコール
a.callB();
上記のような場合だと、クラスAのコンストラクタでクラスBのインスタンスを生成しているため、クラスAとクラスBが強く結合してしまっている状態です。
その為、クラスBが変更されるとクラスAを変更しなければならない可能性が非常に高くなっています。
そこで、以下のようにクラスBのインスタンスを外部から注入するように変更します。
クラスAにクラスBのインスタンスを外部から注入した例
class ClassB {
public log(): void {
console.log('Hello, World!');
}
}
class ClassA {
private b: ClassB;
constructor(b: ClassB) {
// クラスBのインスタンスが渡されているため、クラスAの中でインスタンス化しなくていい
this.b = b;
}
public callB(): void {
this.b.log();
}
}
const b = new ClassB();
// クラスBのインスタンスをクラスAに渡す
const a = new ClassA(b);
a.callB();
上記の例では、クラスAのコンストラクタにクラスBのインスタンスを渡すようにしました。
これによって、クラスAは外部からクラスBのインスタンスを注入することができるようになりました。
このようにDependency Injectionを利用することで、クラス間の結合度を低くし、柔軟性を持たせることができます。また、テストコードを書く際にも、テスト用の依存性を差し替えることで、依存関係があるクラスのメソッドを単体でテストできます。
さらに、Dependency Injectionは、アプリケーション全体で共通する機能を提供するサービスを実装する場合にも役立ちます。例えば、ログ出力機能を提供するLoggerサービスを作成し、アプリケーション内の複数のクラスで利用することもできます。