Service Locator
Overview
サービスロケーターパターン(Service Locator Pattern)は、オブジェクトが依存するサービスを取得するための「中央のアクセスポイント」を提供する設計パターン。
注記
サービスロケーターパターン(Service Locator Pattern)は、GoF(Gang of Four)のオブジェクト指向デザインパターンには属していない。
ヒント
サービスロケータパターンは、手軽に依存関係を管理できるけど、使いすぎると依存関係が不透明になりがち
「シンプルなプロジェクトで、簡単に依存を解決したいとき」に向いてるけど、大規模開発では依存性注入(DI)を使う方がベター
ReactならuseContext、モバイルならGetIt(Flutter)やDagger(Android)みたいなDIツールを活用すると良い
なぜサービスロケーターパターンが必要なのか?
-
依存関係の管理を簡単にする オブジェクトを作るときに、たくさんの依存関係をコンストラクタで渡すとコードが複雑になる。 そこで、サービスロケータを使って必要なときに依存を取得するという方法を取る。
-
DIコンテナの代替として使える 依存性の注入(DI: Dependency Injection)とは異なり、明示的に依存を注入するのではなく、ロケータを通じて取得する。
サービスロケータの仕組み
サービスロケータは、以下のような仕組みで動作する。
- サービスを登録(「このサービスはこのクラスのインスタンスだよ」と設定)
- クライアント(利用する側)がサービスを取得(ロケータを通じてサービスを呼び出す)
実装例
// サービスのインターフェース
interface ApiService {
fetchData(): string;
}
// 具体的な実装
class ApiServiceImpl implements ApiService {
fetchData() {
return "データ取得!";
}
}
// サービスロケータの実装
class ServiceLocator {
private static services = new Map<string, any>();
static register<T>(name: string, instance: T) {
this.services.set(name, instance);
}
static get<T>(name: string): T {
const service = this.services.get(name);
if (!service) {
throw new Error(`サービス ${name} が見つかりません`);
}
return service;
}
}
// サービスを登録
ServiceLocator.register<ApiService>("ApiService", new ApiServiceImpl());
// サービスを取得して使用
const apiService = ServiceLocator.get<ApiService>("ApiService");
console.log(apiService.fetchData()); // "データ取得!"
サービスロケータとDI(依存性注入)の違い
サービスロケータは導入が簡単だけど、依存関係の管理が難しくなるデメリット。
サービスロケータを使うべきか?
使うのが適しているケース。
- シンプルなプロジェクトで手軽にDIっぽい仕組みを導入したい
- オブジェクトの生成や管理を統一したい
- モバイルアプリ(FlutterのGetItなど)やバックエンド(SpringのServiceLocatorFactoryBeanなど)でよく使われる
避けるべきケース。
- 大規模プロジェクトで依存関係が複雑になりそう
- テストをしやすくしたい場合(DIの方がモックの入れ替えが簡単)
- Reactのようなフレームワークでは、useContextやProviderを使う方が適切