Padrão do Localizador de Serviço

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.