Перейти к основному содержимому

Шаблон Service Locator и реализация Java

· 4 мин. чтения

1. Введение

В этом руководстве мы собираемся узнать о шаблоне проектирования Service Locator в Java .

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

2. Понимание шаблона

Целью шаблона Service Locator является возврат экземпляров службы по запросу. Это полезно для отделения потребителей услуг от конкретных классов.

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

  • Клиент – клиентский объект является потребителем услуги. Он отвечает за вызов запроса от локатора сервисов.
  • Service Locator — это точка входа для возврата сервисов из кеша.
  • Кэш — объект для хранения ссылок на сервисы для их повторного использования в дальнейшем.
  • Инициализатор — создает и регистрирует ссылки на сервисы в кеше.
  • Служба — компонент службы представляет исходные службы или их реализацию.

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

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<MessagingService> 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() он сначала проверит кеш на наличие экземпляра службы.

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

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

В первый раз, когда мы получаем EmailService из ServiceLocator , создается и возвращается новый экземпляр . Затем после его вызова в следующий раз из кеша будет возвращен EmailService .

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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