1. 序章

このチュートリアルでは、JavaServiceLocatorデザインパターンについて学習します。

概念を説明し、例を実装し、その使用の長所と短所を強調します。

2. パターンを理解する

Service Locatorパターンの目的は、サービスインスタンスをオンデマンドで返すことです。これは、サービスコンシューマーを具象クラスから切り離すのに役立ちます。

実装は、次のコンポーネントで構成されます。

  • クライアント–クライアントオブジェクトはサービスコンシューマーです。 サービスロケーターからのリクエストを呼び出す責任があります
  • Service Locator –キャッシュからサービスを返すための通信エントリポイントです
  • キャッシュ–後で再利用するためにサービス参照を保存するためのオブジェクト
  • イニシャライザー–キャッシュ内のサービスへの参照を作成して登録します
  • サービス–サービスコンポーネントは、元のサービスまたはその実装を表します

元のサービスオブジェクトはロケーターによって検索され、オンデマンドで返されます。

3. 実装

それでは、実用的になって、例を通して概念を見てみましょう。

まず、さまざまな方法でメッセージを送信するためのMessagingServiceインターフェイスを作成します。

public interface MessagingService {

    String getMessageBody();
    String getServiceName();
}

次に、電子メールとSMSを介してメッセージを送信する上記のインターフェイスの2つの実装を定義します。

public class EmailService implements MessagingService {

    public String getMessageBody() {
        return "email message";
    }

    public String getServiceName() {
        return "EmailService";
    }
}

SMSService クラスの定義は、EmailServiceクラスに似ています。

2つのサービスを定義した後、それらを初期化するロジックを定義する必要があります。

public class InitialContext {
    public Object lookup(String serviceName) {
        if (serviceName.equalsIgnoreCase("EmailService")) {
            return new EmailService();
        } else if (serviceName.equalsIgnoreCase("SMSService")) {
            return new SMSService();
        }
        return null;
    }
}

サービスロケーターオブジェクトをまとめる前に必要な最後のコンポーネントはキャッシュです。

この例では、これはListプロパティを持つ単純なクラスです。

public class Cache {
    private List<MessagingService> services = new ArrayList<>();

    public MessagingService getService(String serviceName) {
        // retrieve from the list
    }

    public void addService(MessagingService newService) {
        // add to the list
    }
}

最後に、サービスロケータークラスを実装できます。

public class ServiceLocator {

    private static Cache cache = new Cache();

    public static MessagingService getService(String serviceName) {

        MessagingService service = cache.getService(serviceName);

        if (service != null) {
            return service;
        }

        InitialContext context = new InitialContext();
        MessagingService service1 = (MessagingService) context
          .lookup(serviceName);
        cache.addService(service1);
        return service1;
    }
}

ここでのロジックはかなり単純です。

クラスはCacheのインスタンスを保持します。次に、 getService()メソッドで、最初にサービスのインスタンスのキャッシュをチェックします。

次に、それが null、の場合、初期化ロジックを呼び出し、新しいオブジェクトをキャッシュに追加します。

4. テスト

ここでインスタンスを取得する方法を見てみましょう。

MessagingService service 
  = ServiceLocator.getService("EmailService");
String email = service.getMessageBody();

MessagingService smsService 
  = ServiceLocator.getService("SMSService");
String sms = smsService.getMessageBody();

MessagingService emailService 
  = ServiceLocator.getService("EmailService");
String newEmail = emailService.getMessageBody();

ServiceLocatorからEmailServiceを初めて取得すると、新しいインスタンスが作成されて返されます。 次に呼び出した後、EmailServiceがキャッシュから返されます。

5. サービスロケーターと依存性注入

一見すると、Service Locatorパターンは、別のよく知られたパターン、つまり依存性注入に似ているように見えます。

まず、依存性注入とサービスロケーターパターンの両方が制御の反転の概念の実装であることに注意することが重要です。

先に進む前に、この記事で依存性注入について詳しく学んでください。

ここでの主な違いは、クライアントオブジェクトが引き続き依存関係を作成することです。 そのためにロケーターを使用するだけです。つまり、ロケーターオブジェクトへの参照が必要です。

比較すると、依存性注入を使用する場合、クラスには依存性が与えられます。 インジェクターは、クラスに依存関係を注入するために、起動時に1回だけ呼び出されます。

最後に、ServiceLocatorパターンの使用を避けるいくつかの理由を考えてみましょう。

それに対する1つの議論は、ユニットテストを困難にするということです。 依存性注入を使用すると、依存クラスのモックオブジェクトをテスト対象のインスタンスに渡すことができます。 一方、これはServiceLocatorパターンのボトルネックです。

もう1つの問題は、このパターンに基づいてAPIを使用するのが難しいことです。 これは、依存関係がクラス内に隠されており、実行時にのみ検証されるためです。

これらすべてにもかかわらず、Service Locatorパターンはコーディングと理解が容易であり、小規模なアプリケーションに最適です。

6. 結論

このガイドでは、ServiceLocatorデザインパターンを使用する方法と理由を示します。 ServiceLocatorのデザインパターンと依存性注入の概念の主な違いについて説明します。

一般に、アプリケーションでクラスを設計する方法を選択するのは開発者次第です。

Service Locatorパターンは、コードを分離するための簡単なパターンです。 ただし、複数のアプリケーションでクラスを使用する場合は、依存性注入が正しい選択です。

いつものように、完全なコードはGithubプロジェクトで入手できます。