Service Locator Pattern und Java-Implementierung

1. Einleitung

In diesem Tutorial lernen wir das Entwurfsmuster von Service Locator in Java kennen .

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

2. Das Muster verstehen

Der Zweck des Service Locator-Musters besteht darin, die Service-Instanzen bei Bedarf zurückzugeben. 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 Dienstreferenzen, um sie später wiederzuverwenden
  • Initializer - Erstellt und registriert Verweise auf Dienste im Cache
  • Service - Die Servicekomponente repräsentiert die ursprünglichen Services oder deren Implementierung

Das ursprüngliche Serviceobjekt wird vom Locator nachgeschlagen und bei Bedarf zurückgegeben.

3. Implementierung

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

Zunächst erstellen wir eine MessagingService- 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 SMSService- Klassendefinition ähnelt der EmailService- Klasse.

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 benötigen, bevor wir das Service Locator-Objekt zusammensetzen, ist der Cache.

In unserem Beispiel ist dies eine einfache Klasse mit einer List- Eigenschaft:

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 des Cache. Anschließend überprüft es in der Methode getService () zunächst den Cache auf eine Instanz des Dienstes.

Wenn dies null 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();

Das erste Mal , erhalten wir den Emailservice von dem Servicelocator eine neue Instanz erstellt und zurückgegeben . Nach dem nächsten Aufruf wird der EmailService aus dem Cache zurückgegeben.

5. Service Locator vs Dependency Injection

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

Zunächst ist zu beachten, dass sowohl die Abhängigkeitsinjektion als auch das Service Locator-Muster Implementierungen des Inversion of Control-Konzepts sind .

Bevor Sie fortfahren, erfahren Sie in diesem Artikel mehr über Dependency Injection.

Der Hauptunterschied besteht darin, dass das Client-Objekt weiterhin seine Abhängigkeiten erstellt . 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 es Unit-Tests schwierig macht. 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 leicht zu codieren und zu verstehen und kann eine gute Wahl für kleine Anwendungen sein.

6. Fazit

Diese Anleitung zeigt, wie und warum das Service Locator-Entwurfsmuster verwendet wird. Es werden die wichtigsten Unterschiede zwischen dem Service Locator-Entwurfsmuster und dem Abhängigkeitsinjektionskonzept erörtert.

Im Allgemeinen muss der Entwickler entscheiden, wie die Klassen in der Anwendung gestaltet 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.

Der vollständige Code ist wie gewohnt im Github-Projekt verfügbar.