Шаблон сервисного локатора

Шаблон сервисного локатора

1. Вступление

В этом руководстве мы узнаем оthe Service Locator design pattern in Java.

Опишем концепцию, реализуем на примере и выделим плюсы и минусы ее использования.

2. Понимание паттерна

The purpose of the Service Locator pattern is to return the service instances on demand. Это полезно для отделения потребителей услуг от конкретных классов.

Реализация будет состоять из следующих компонентов:

  • Клиент - клиентский объект является потребителем сервиса. Он отвечает за выполнение запроса от локатора служб.

  • Сервисный локатор - это точка входа связи для возврата сервисов из кеша

  • Кэш - объект для хранения сервисных ссылок, чтобы использовать их позже

  • Initializer - создает и регистрирует ссылки на сервисы в кеше

  • Сервис - компонент Сервис представляет собой оригинальные сервисы или их реализацию

Исходный объект службы ищется локатором и возвращается по требованию.

3. Реализация

Теперь давайте приступим к практическим действиям и рассмотрим концепции на примере.

Сначала мы создадим интерфейсMessagingService для отправки сообщений разными способами:

public interface MessagingService {

    String getMessageBody();
    String getServiceName();
}

Затем мы определим две реализации интерфейса выше, которые отправляют сообщения по электронной почте и SMS:

public class EmailService implements MessagingService {

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

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

Определение классаSMSService аналогично классуEmailService.

После определения двух сервисов мы должны определить логику для их инициализации:

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

Последний компонент, который нам нужен перед тем, как собрать объект локатора службы, - это кэш.

В нашем примере это простой класс со свойством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
    }
}

Наконец, мы можем реализовать наш класс локатора сервиса:

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

Логика здесь довольно проста.

Класс содержит экземплярCache.. Затем, в методеgetService(), он сначала проверит кеш для экземпляра службы.

Затем, если этоnull,, он вызовет логику инициализации и добавит новый объект в кеш.

4. тестирование

Посмотрим, как теперь получить экземпляры:

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. Затем, после его вызова в следующий раз,EmailService будет возвращен из кеша.

5. Локатор сервисов против внедрения зависимостей

На первый взгляд, шаблон Service Locator может выглядеть похожим на другой известный шаблон, а именно Inpendency Injection.

Во-первых, важно отметить, чтоboth Dependency Injection and the Service Locator pattern are implementations of the Inversion of Control concept.

Прежде чем двигаться дальше, узнайте больше о внедрении зависимостей в этомwrite-up.

The key difference here is that the client object still creates its dependencies. Для этого он просто использует локатор, а это означает, что ему нужна ссылка на объект локатора.

Для сравнения, при использовании внедрения зависимостей классу присваиваются зависимости. Инжектор вызывается только один раз при запуске, чтобы ввести зависимости в класс.

Наконец, давайте рассмотрим несколько причин, по которым следует избегать использования шаблона Service Locator.

Одним из аргументов против этого является то, что это затрудняет юнит-тестирование. С помощью внедрения зависимостей мы можем передавать фиктивные объекты зависимого класса в тестируемый экземпляр. С другой стороны, это узкое место с шаблоном Service Locator.

Другая проблема заключается в том, что использовать API, основанные на этом шаблоне, сложнее. Причина в том, что зависимости скрыты внутри класса и проверяются только во время выполнения.

Несмотря на все это, шаблон Service Locator легко кодировать и понимать, и может быть отличным выбором для небольших приложений.

6. Заключение

В этом руководстве показано, как и зачем использовать шаблон проектирования Service Locator. В нем рассматриваются ключевые различия между шаблоном проектирования Service Locator и концепцией внедрения зависимостей.

В общем, разработчик сам должен выбрать способ проектирования классов в приложении.

Шаблон Service Locator - это простой шаблон для разделения кода. Однако в случае использования классов в нескольких приложениях внедрение зависимостей является правильным выбором.

Как обычно, полный код доступен вGithub project.