Что такое паттерн Сервис Локатор (Service Locator)?

«Что такое паттерн Сервис Локатор (Service Locator)?» — вопрос из категории Паттерны проектирования, который задают на 28% собеседований AQA / Automation. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Сервис Локатор (Service Locator) — это паттерн проектирования, который выступает в роли централизованного реестра, предоставляющего доступ к различным сервисам (зависимостям) приложения. Вместо того чтобы создавать сервисы напрямую, клиентский код запрашивает их у локатора. С точки зрения QA, понимание этого паттерна важно для тестирования конфигурации и интеграции компонентов.

Основная идея: Скрыть сложность создания и нахождения сервисов, уменьшая связанность между классами.

Пример простой реализации на Java:

import java.util.HashMap;
import java.util.Map;

// Класс-локатор сервисов
public class ServiceLocator {
    private static final Map<Class<?>, Object> services = new HashMap<>();

    // Регистрация сервиса в локаторе
    public static <T> void registerService(Class<T> interfaceClass, T implementation) {
        services.put(interfaceClass, implementation);
    }

    // Получение сервиса из локатора
    public static <T> T getService(Class<T> interfaceClass) {
        return (T) services.get(interfaceClass);
    }
}

// Интерфейс и его реализация
interface Logger {
    void log(String message);
}

class FileLogger implements Logger {
    @Override
    public void log(String message) {
        System.out.println("Log to file: " + message);
    }
}

// Использование в коде приложения
public class MyApp {
    public void doWork() {
        Logger logger = ServiceLocator.getService(Logger.class);
        logger.log("Application started");
    }
}

// Конфигурация (обычно при старте приложения)
class AppConfig {
    public static void configure() {
        ServiceLocator.registerService(Logger.class, new FileLogger());
    }
}

Влияние на процесс тестирования (QA):

  • Плюс для модульного тестирования: В тестах можно легко подменить реальный сервис на mock- или stub-объект, зарегистрировав его в локаторе перед запуском теста.

    @Test
    public void testDoWork() {
        // Подменяем реальный логгер на заглушку
        Logger mockLogger = Mockito.mock(Logger.class);
        ServiceLocator.registerService(Logger.class, mockLogger);
    
        MyApp app = new MyApp();
        app.doWork();
    
        // Проверяем, что метод log был вызван
        Mockito.verify(mockLogger).log("Application started");
    }
  • Минус — скрытые зависимости: Паттерн делает зависимости неявными. Класс MyApp не показывает, что ему нужен Logger, что усложняет анализ кода и может привести к ошибкам времени выполнения, если сервис не был зарегистрирован. Это критичный момент для интеграционного тестирования — нужно проверять корректность конфигурации локатора.
  • Альтернатива — Внедрение зависимостей (DI): В современных фреймворках (Spring, Guice) чаще используется DI, где зависимости передаются явно (через конструктор или сеттер), что делает код более прозрачным и удобным для тестирования.