Ответ
Оба подхода решают проблему создания зависимостей, но делают это принципиально по-разному, что влияет на тестируемость и прозрачность кода.
Dependency Injection (DI, Внедрение зависимостей): Зависимости явно передаются классу извне (через конструктор, метод или свойство). Класс ничего не знает о том, как создаются его зависимости.
// Зависимости передаются явно через конструктор.
public class UserService {
private final UserRepository userRepository;
private final EmailService emailService;
// Зависимости ВНЕДРЯЮТСЯ. Код ясно показывает, что нужно для работы сервиса.
public UserService(UserRepository userRepository, EmailService emailService) {
this.userRepository = userRepository;
this.emailService = emailService;
}
public void registerUser(String email) {
User user = new User(email);
userRepository.save(user);
emailService.sendWelcomeEmail(user);
}
}
// Тестирование простое: передаем моки-заглушки.
UserRepository mockRepo = mock(UserRepository.class);
EmailService mockEmail = mock(EmailService.class);
UserService service = new UserService(mockRepo, mockEmail);
Service Locator (SL, Локатор служб): Класс сам запрашивает свои зависимости из некоего глобального контейнера или реестра.
public class UserService {
// Зависимости скрыты. Непонятно, что нужно для работы класса, не заглядывая в метод.
public void registerUser(String email) {
// Класс активно обращается к глобальному локатору.
UserRepository userRepository = ServiceLocator.getService(UserRepository.class);
EmailService emailService = ServiceLocator.getService(EmailService.class);
User user = new User(email);
userRepository.save(user);
emailService.sendWelcomeEmail(user);
}
}
// Тестирование сложнее: нужно настроить глобальный ServiceLocator, чтобы он возвращал моки.
Ключевая проблема Service Locator:
- Скрытые зависимости. Невозможно понять зависимости класса, глядя только на его публичный интерфейс (сигнатуру конструктора/методов). Нужно анализировать весь код.
- Усложнение тестирования. Тест зависит от глобального состояния (локатора), который нужно правильно настроить и очистить.
- Нарушение принципа инверсии зависимостей (DIP). Класс зависит от конкретной реализации локатора.
Вывод: DI является предпочтительным паттерном, так как он делает зависимости явными, код — более понятным, тестируемым и соответствующим принципам SOLID. Service Locator считается антипаттерном в контексте управления зависимостями, хотя может быть полезен в некоторых специфических сценариях (например, для разрешения зависимостей внутри фабрик).