メインコンテンツまでスキップ

Service Locator

Overview

サービスロケーターパターン(Service Locator Pattern)は、オブジェクトが依存するサービスを取得するための「中央のアクセスポイント」を提供する設計パターン。

注記

サービスロケーターパターン(Service Locator Pattern)は、GoF(Gang of Four)のオブジェクト指向デザインパターンには属していない。

ヒント

サービスロケータパターンは、手軽に依存関係を管理できるけど、使いすぎると依存関係が不透明になりがち
「シンプルなプロジェクトで、簡単に依存を解決したいとき」に向いてるけど、大規模開発では依存性注入(DI)を使う方がベター ReactならuseContext、モバイルならGetIt(Flutter)やDagger(Android)みたいなDIツールを活用すると良い

なぜサービスロケーターパターンが必要なのか?

  1. 依存関係の管理を簡単にする オブジェクトを作るときに、たくさんの依存関係をコンストラクタで渡すとコードが複雑になる。 そこで、サービスロケータを使って必要なときに依存を取得するという方法を取る。

  2. DIコンテナの代替として使える 依存性の注入(DI: Dependency Injection)とは異なり、明示的に依存を注入するのではなく、ロケータを通じて取得する。

サービスロケータの仕組み

サービスロケータは、以下のような仕組みで動作する。

  1. サービスを登録(「このサービスはこのクラスのインスタンスだよ」と設定)
  2. クライアント(利用する側)がサービスを取得(ロケータを通じてサービスを呼び出す)

実装例

// サービスのインターフェース
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を使う方が適切