Patrón de localizador de servicios e implementación de Java

1. Introducción

En este tutorial, aprenderemos sobre el patrón de diseño del localizador de servicios en Java .

Describiremos el concepto, implementaremos un ejemplo y destacaremos los pros y los contras de su uso.

2. Comprensión del patrón

El propósito del patrón del localizador de servicios es devolver las instancias de servicio a pedido. Esto es útil para desvincular a los consumidores de servicios de clases concretas.

Una implementación constará de los siguientes componentes:

  • Cliente: el objeto del cliente es un consumidor de servicios. Es responsable de invocar la solicitud desde el localizador de servicios.
  • Localizador de servicios: es un punto de entrada de comunicación para devolver los servicios desde la caché.
  • Caché: un objeto para almacenar referencias de servicio para reutilizarlas más tarde
  • Inicializador: crea y registra referencias a servicios en la caché
  • Servicio: el componente Servicio representa los servicios originales o su implementación.

El localizador busca el objeto de servicio original y lo devuelve a pedido.

3. Implementación

Ahora, seamos prácticos y veamos los conceptos a través de un ejemplo.

Primero, crearemos una interfaz MessagingService para enviar mensajes de diferentes formas:

public interface MessagingService { String getMessageBody(); String getServiceName(); }

A continuación, definiremos dos implementaciones de la interfaz anterior, que envían mensajes por correo electrónico y SMS:

public class EmailService implements MessagingService { public String getMessageBody() { return "email message"; } public String getServiceName() { return "EmailService"; } }

La definición de la clase SMSService es similar a la clase EmailService .

Después de definir los dos servicios, tenemos que definir la lógica para inicializarlos:

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

El último componente que necesitamos antes de juntar el objeto del localizador de servicios es el caché.

En nuestro ejemplo, esta es una clase simple con una propiedad List :

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

Finalmente, podemos implementar nuestra clase de localizador de servicios:

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

La lógica aquí es bastante simple.

La clase contiene una instancia de la caché. Luego, en el método getService () , primero verificará en la caché una instancia del servicio.

Luego, si es nulo, llamará a la lógica de inicialización y agregará el nuevo objeto a la caché.

4. Prueba

Veamos cómo podemos obtener instancias ahora:

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

La primera vez que obtenemos el EmailService del ServiceLocator, se crea y devuelve una nueva instancia . Luego, después de llamarlo la próxima vez, EmailService se devolverá desde la caché.

5. Localizador de servicios frente a inyección de dependencia

A primera vista, el patrón del localizador de servicios puede parecer similar a otro patrón conocido, a saber, la inyección de dependencia.

Primero, es importante tener en cuenta que tanto la inyección de dependencia como el patrón del localizador de servicios son implementaciones del concepto de inversión de control .

Antes de continuar, obtenga más información sobre la inyección de dependencia en este artículo.

La diferencia clave aquí es que el objeto cliente aún crea sus dependencias . Solo usa el localizador para eso, lo que significa que necesita una referencia al objeto localizador.

En comparación, cuando se usa la inyección de dependencias, la clase recibe las dependencias. El inyector se llama solo una vez al inicio para inyectar dependencias en la clase.

Finalmente, consideremos algunas razones para evitar usar el patrón de localizador de servicios.

Un argumento en contra es que dificulta las pruebas unitarias. Con la inyección de dependencia, podemos pasar objetos simulados de la clase dependiente a la instancia probada. Por otro lado, este es un cuello de botella con el patrón de localizador de servicios.

Otro problema es que es más complicado usar API basadas en este patrón. La razón de esto es que las dependencias están ocultas dentro de la clase y solo se verifican en tiempo de ejecución.

A pesar de todo esto, el patrón del localizador de servicios es fácil de codificar y comprender, y puede ser una excelente opción para aplicaciones pequeñas.

6. Conclusión

Esta guía muestra cómo y por qué utilizar el patrón de diseño del localizador de servicios. Se analizan las diferencias clave entre el patrón de diseño del localizador de servicios y el concepto de inyección de dependencia.

En general, depende del desarrollador elegir cómo diseñar las clases en la aplicación.

El patrón del localizador de servicios es un patrón sencillo para desacoplar el código. Sin embargo, en caso de usar las clases en múltiples aplicaciones, la inyección de dependencia es una opción correcta.

Como es habitual, el código completo está disponible en el proyecto Github.