Padrão do Localizador de Serviço
1. Introdução
Neste tutorial, aprenderemos sobrethe Service Locator design pattern in Java.
Descreveremos o conceito, implementaremos um exemplo e destacaremos os prós e contras de seu uso.
2. Compreendendo o padrão
The purpose of the Service Locator pattern is to return the service instances on demand. Isso é útil para desacoplar consumidores de serviço de classes concretas.
Uma implementação consistirá nos seguintes componentes:
-
Cliente - o objeto do cliente é um consumidor de serviço. É responsável por invocar a solicitação do localizador de serviço
-
Service Locator - é um ponto de entrada de comunicação para retornar os serviços do cache
-
Cache - um objeto para armazenar referências de serviço para reutilizá-las mais tarde
-
Inicializador - cria e registra referências a serviços no cache
-
Serviço - o componente Serviço representa os serviços originais ou sua implementação
O objeto de serviço original é procurado pelo localizador e retornado sob demanda.
3. Implementação
Agora, vamos ser práticos e dar uma olhada nos conceitos por meio de um exemplo.
Primeiro, vamos criar uma interfaceMessagingService para enviar mensagens de diferentes maneiras:
public interface MessagingService {
String getMessageBody();
String getServiceName();
}
A seguir, definiremos duas implementações da interface acima, que enviam mensagens por e-mail e SMS:
public class EmailService implements MessagingService {
public String getMessageBody() {
return "email message";
}
public String getServiceName() {
return "EmailService";
}
}
A definição da classeSMSService é semelhante à da classeEmailService.
Depois de definir os dois serviços, precisamos definir a lógica para inicializá-los:
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;
}
}
O último componente necessário antes de montar o objeto localizador de serviço é o cache.
Em nosso exemplo, esta é uma classe simples com uma propriedadeList:
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
}
}
Por fim, podemos implementar nossa classe de localizador de serviço:
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;
}
}
A lógica aqui é bastante simples.
A classe contém uma instância deCache. Então, no métodogetService(), ela primeiro verificará o cache para uma instância do serviço.
Então, se fornull,, ele chamará a lógica de inicialização e adicionará o novo objeto ao cache.
4. Teste
Vamos ver como podemos obter instâncias agora:
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. Então, após chamá-lo da próxima vez, oEmailService será retornado do cache.
5. Localizador de serviço vs injeção de dependência
À primeira vista, o padrão do Localizador de Serviço pode parecer semelhante a outro padrão conhecido - a injeção de dependência.
Primeiro, é importante notar queboth Dependency Injection and the Service Locator pattern are implementations of the Inversion of Control concept.
Antes de prosseguir, aprenda mais sobre injeção de dependência nestewrite-up.
The key difference here is that the client object still creates its dependencies. Ele apenas usa o localizador para isso, o que significa que precisa de uma referência ao objeto localizador.
Por comparação, ao usar a injeção de dependência, a classe recebe as dependências. O injetor é chamado apenas uma vez na inicialização para injetar dependências na classe.
Finalmente, vamos considerar alguns motivos para evitar o uso do padrão Service Locator.
Um argumento contra isso é que dificulta o teste de unidade. Com a injeção de dependência, podemos passar objetos simulados da classe dependente para a instância testada. Por outro lado, esse é um gargalo com o padrão Service Locator.
Outro problema é que é mais complicado usar APIs com base neste padrão. A razão para isso é que as dependências estão ocultas dentro da classe e só são verificadas em tempo de execução.
Apesar de tudo isso, o padrão do Localizador de serviço é fácil de codificar e entender e pode ser uma ótima opção para aplicativos pequenos.
6. Conclusão
Este guia mostra como e por que usar o padrão de design do Localizador de Serviço. Ele discute as principais diferenças entre o padrão de design do Localizador de Serviço e o conceito de Injeção de Dependência.
Em geral, cabe ao desenvolvedor escolher como projetar as classes no aplicativo.
O padrão do Localizador de serviço é um padrão simples para dissociar o código. No entanto, no caso de usar as classes em vários aplicativos, a injeção de dependência é uma escolha certa.
Como de costume, o código completo está disponível emGithub project.