
「Dependency Injection(依存性の注入)」は、
オブジェクト同士の結合を緩めて、テストや拡張をしやすくするための設計パターンです。
基本の考え方
・クラスが他のクラス(依存対象)を 自分でnewしない
・外部から「必要な依存オブジェクト」を 注入(Injection) してもらう
・結果として、クラス同士の依存関係を 疎結合(loose coupling) にできる
用語解説
依存(Dependency)
クラスが使う他のオブジェクト。
注入(Injection)
外部からその依存を渡すこと。
目的
クラス間の結合をゆるめ、柔軟でテストしやすい設計にする。
サンプルコード1: Before After
依存が強いコード
class EmailService {
send(message) {
console.log("📧 Email sent:", message);
}
}
class UserNotifier {
constructor() {
this.emailService = new EmailService(); // ← 依存を内部で固定している
}
notify() {
this.emailService.send("Welcome new user!");
}
}
const notifier = new UserNotifier();
notifier.notify();
このコードの問題点
・UserNotifier が EmailService を直接生成している。
・このため、もしSMSやSlack通知に変えたい場合、クラスの中身を書き換える必要がある。
改良版:Dependency Injectionを使う
class EmailService {
send(message) {
console.log("Email sent:", message);
}
}
class SMSService {
send(message) {
console.log("SMS sent:", message);
}
}
class UserNotifier {
constructor(service) {
this.service = service; // ← 依存を外部から注入
}
notify() {
this.service.send("Welcome new user!");
}
}
// 依存の切り替えが自由にできる
const emailNotifier = new UserNotifier(new EmailService());
const smsNotifier = new UserNotifier(new SMSService());
emailNotifier.notify();
smsNotifier.notify();
ポイント解説
・UserNotifier は 抽象的な「sendできるもの」 に依存。
・具体的な通知方法は外から与える。(=依存を注入する)
依存性注入の3つの方法
| 方法 |
説明 |
| コンストラクタ注入 |
コンストラクタの引数で依存を受け取る(最も一般的) |
| セッター注入 |
`setService()` のようなメソッドで後から注入 |
| インターフェース注入 |
外部のDIコンテナ(例:Spring)によって自動的に注入 |
サンプルコード2 : DIコンテナ風実装
class Container {
constructor() {
this.bindings = new Map();
}
register(key, ClassRef) {
this.bindings.set(key, ClassRef);
}
resolve(key) {
const ClassRef = this.bindings.get(key);
return new ClassRef();
}
}
// 依存を登録
const di = new Container();
di.register("notificationService", EmailService);
di.register("userNotifier", () => new UserNotifier(di.resolve("notificationService")));
// 依存を自動解決して利用
const notifier = di.resolve("userNotifier");
notifier.notify();
メリット
・クラス間の結合度が低くなる(差し替えが容易)
・テストがしやすくなる(モックやスタブを注入可能)
・アプリケーションの拡張性が高まる
デメリット
・小規模アプリでは逆に複雑になる
・依存関係の把握が難しくなる(DIコンテナを多用すると特に)
・ランタイムエラーになりやすい(型安全ではない言語では注意)
あとがき
プログラミングの設計で重要なのは、なんといっても「依存度を下げる」という点でしょう。
Dependency Injectionは、まさにそのための設計思想パターンですね。
できる限りクラス分けして、それぞれを独立させることで、その後の拡張などが便利になるのが、サンプルコードを見て理解できると思います。
ただ、DIコンテナというクラス構造の階層が深くなるリスクがあるので、全体設計を事前にしてから、DIコンテナ利用を設置するのはどこが適切かを見極めないと、逆に分かりずらく、管理しずらい設計になりかねないという点を、理解しておく必要もあります。
そんな加減が必要なデザインパターンでした。
0 件のコメント:
コメントを投稿