
Service Locator(サービスロケータ)パターン は、Dependency Injection(DI)と密接に関係するもう1つの依存解決パターンです。
両者は似ていますが、依存を「どう渡すか」という点で方向性が真逆です。
オブジェクトが必要なサービスを「探しに行く」仕組みを中央化して、
アプリ全体で使うサービスを1つの「ロケータ(ServiceLocator)」が管理します。
必要な時に ServiceLocator.get("Service名") のようにして取得するのが特徴です。
構造イメージ
[ ServiceLocator ]
↓ get()
┌─────────────
│ UserNotifier
└─────────────
↑
EmailService
サンプルコード
依存を直接newしている悪い例
class EmailService {
send(msg) {
console.log("Email:", msg);
}
}
class UserNotifier {
constructor() {
this.service = new EmailService(); // ← 強い依存
}
notify() {
this.service.send("Welcome new user!");
}
}
Service Locatorを使う例
class ServiceLocator {
static services = new Map();
static register(name, instance) {
this.services.set(name, instance);
}
static get(name) {
return this.services.get(name);
}
}
// サービス定義
class EmailService {
send(msg) {
console.log("Email:", msg);
}
}
// サービス登録
ServiceLocator.register("EmailService", new EmailService());
// クライアント
class UserNotifier {
constructor() {
this.service = ServiceLocator.get("EmailService"); // ← 依存を取得
}
notify() {
this.service.send("Welcome new user!");
}
}
// 実行
new UserNotifier().notify();
DIとの違い
| 項目 |
Dependency Injection |
Service Locator |
| 依存の渡し方 |
外部から注入される |
自分で取りに行く |
| 主導権 |
外部(呼び出し側) |
クラス自身 |
| テスト容易性 |
高い(モック差し替え容易) |
低い(隠れ依存になりやすい) |
| 柔軟性 |
高い(構成変更しやすい) |
中程度(登録の書き換えで対応) |
| コードの明示性 |
明確 |
不明瞭(内部でgetされるため) |
メリット
・コードを重複削減することができる。
・構造の中央化を行い、効率化できる。
デメリット
・依存関係が隠蔽される
→ コードを見ただけでは何を使っているか分かりにくい
・テストが難しくなる
→ モック注入がグローバルに影響する
・設計がアンチパターン化しやすい
→ 「何でもServiceLocatorから取る」状態になり、結局密結合に近づく
適用の目安
○ 小規模 or 実行環境が限定されるアプリ
○ 簡易的な依存解決を手軽に行いたい場合
× 大規模・テスト重視のアプリ(→ DI推奨)
あとがき
いつも、class構造を考えるときに、staticメソッドにするか、privateメソッドにするか悩む時があります。
他のクラスからのメソッドアクセスを柔軟にするか、アクセス不可にするか、newインスタンスでオブジェクトかするか、
そんな、他クラスからのアクセスを重視するのが、このService Locatorですね。
DIと真逆のパターンというので覚えやすいかもです。
それぞれを、使いこなしてこそ、設計の達人に近づけるポイントですね。
0 件のコメント:
コメントを投稿