Service Locator Pattern

Service Locator Pattern

1. Einführung

In diesem Tutorial lernen wirthe Service Locator design pattern in Java kennen.

Wir werden das Konzept beschreiben, ein Beispiel implementieren und die Vor- und Nachteile seiner Verwendung hervorheben.

2. Das Muster verstehen

The purpose of the Service Locator pattern is to return the service instances on demand. Dies ist nützlich, um Service-Konsumenten von konkreten Klassen zu entkoppeln.

Eine Implementierung besteht aus folgenden Komponenten:

  • Client - Das Client-Objekt ist ein Service-Consumer. Es ist dafür verantwortlich, die Anforderung vom Service Locator aufzurufen

  • Service Locator - ist ein Kommunikationseinstiegspunkt für die Rückgabe der Dienste aus dem Cache

  • Cache - Ein Objekt zum Speichern von Servicereferenzen, um sie später wiederzuverwenden

  • Initialisierer - Erstellt und registriert Verweise auf Dienste im Cache

  • Service - Die Service-Komponente repräsentiert die ursprünglichen Services oder deren Implementierung

Das ursprüngliche Serviceobjekt wird vom Locator nachgeschlagen und auf Anforderung zurückgegeben.

3. Implementierung

Lassen Sie uns nun praktisch werden und die Konzepte anhand eines Beispiels betrachten.

Zunächst erstellen wir eineMessagingService-Schnittstelle zum Senden von Nachrichten auf verschiedene Arten:

public interface MessagingService {

    String getMessageBody();
    String getServiceName();
}

Als Nächstes definieren wir zwei Implementierungen der obigen Schnittstelle, die Nachrichten per E-Mail und SMS senden:

public class EmailService implements MessagingService {

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

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

Die Klassendefinition vonSMSServiceähnelt der Klasse vonEmailService.

Nachdem wir die beiden Services definiert haben, müssen wir die Logik definieren, um sie zu initialisieren:

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;
    }
}

Die letzte Komponente, die wir vor dem Zusammensetzen des Service Locator-Objekts benötigen, ist der Cache.

In unserem Beispiel ist dies eine einfache Klasse mit der EigenschaftList:

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

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

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

Schließlich können wir unsere Service Locator-Klasse implementieren:

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;
    }
}

Die Logik hier ist ziemlich einfach.

Die Klasse enthält eine Instanz vonCache.. Anschließend überprüft sie in der MethodegetService() zuerst den Cache auf eine Instanz des Dienstes.

Wenn diesnull,ist, wird die Initialisierungslogik aufgerufen und das neue Objekt zum Cache hinzugefügt.

4. Testen

Mal sehen, wie wir jetzt Instanzen erhalten können:

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();

The first time we get the EmailService from the ServiceLocator a new instance is created and returned. Nach dem nächsten Aufruf werden dieEmailService aus dem Cache zurückgegeben.

5. Service Locator vs Abhängigkeitsinjektion

Auf den ersten Blick ähnelt das Service Locator-Muster möglicherweise einem anderen bekannten Muster - der Abhängigkeitsinjektion.

Zunächst ist zu beachten, dassboth Dependency Injection and the Service Locator pattern are implementations of the Inversion of Control concept.

Bevor Sie fortfahren, erfahren Sie mehr über die Abhängigkeitsinjektion inwrite-up.

The key difference here is that the client object still creates its dependencies. Dafür wird nur der Locator verwendet, dh es wird ein Verweis auf das Locator-Objekt benötigt.

Im Vergleich dazu erhält die Klasse bei Verwendung der Abhängigkeitsinjektion die Abhängigkeiten. Der Injektor wird beim Start nur einmal aufgerufen, um Abhängigkeiten in die Klasse zu injizieren.

Lassen Sie uns abschließend einige Gründe betrachten, um die Verwendung des Service Locator-Musters zu vermeiden.

Ein Argument dagegen ist, dass dies das Testen von Einheiten erschwert. Mit der Abhängigkeitsinjektion können wir Scheinobjekte der abhängigen Klasse an die getestete Instanz übergeben. Auf der anderen Seite ist dies ein Engpass mit dem Service Locator-Muster.

Ein weiteres Problem ist, dass es schwieriger ist, APIs zu verwenden, die auf diesem Muster basieren. Der Grund dafür ist, dass die Abhängigkeiten in der Klasse versteckt sind und nur zur Laufzeit überprüft werden.

Trotz alledem ist das Service Locator-Muster einfach zu codieren und zu verstehen und kann eine gute Wahl für kleine Anwendungen sein.

6. Fazit

Dieses Handbuch zeigt, wie und warum das Service Locator-Entwurfsmuster verwendet wird. Es werden die wichtigsten Unterschiede zwischen dem Service Locator-Entwurfsmuster und dem Konzept der Abhängigkeitsinjektion erläutert.

Im Allgemeinen muss der Entwickler entscheiden, wie die Klassen in der Anwendung entworfen werden sollen.

Das Service Locator-Muster ist ein einfaches Muster zum Entkoppeln des Codes. Wenn Sie die Klassen jedoch in mehreren Anwendungen verwenden, ist die Abhängigkeitsinjektion die richtige Wahl.

Wie üblich ist der vollständige Code inGithub project verfügbar.