← Назад к вопросам

Какие принципы SOLID нарушает Service Locator?

3.0 Senior🔥 111 комментариев
#Архитектура и паттерны#ООП

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Нарушение принципов SOLID в Service Locator

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

### 1. Нарушение Принципа единой ответственности (SRP)

Service Locator берет на себя две ответственности:

  • Предоставление зависимостей (его основная функция)
  • Скрытие логики их создания и хранения (глобальное состояние)
class UserController {
    public function create() {
        // Локатор управляет ВСЕМИ зависимостями приложения
        $validator = ServiceLocator::get(Validator::class);
        $mailer = ServiceLocator::get(Mailer::class);
        $db = ServiceLocator::get(Database::class);
        
        // Бизнес-логика смешивается с получением зависимостей
        $user = new User($db);
        $user->save();
    }
}

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

### 2. Нарушение Принципа открытости/закрытости (OCP)

Для расширения функциональности (добавления новой зависимости) необходимо модифицировать существующий код, а не расширять его:

class PaymentService {
    public function process() {
        $processor = ServiceLocator::get(PaymentProcessor::class);
        
        // Если понадобится добавить логирование:
        // Придется изменять этот метод
        // $logger = ServiceLocator::get(Logger::class);
    }
}

Внедрение зависимостей через конструктор (Dependency Injection) решает эту проблему, позволяя добавлять новые зависимости без изменения существующего кода.

### 3. Нарушение Принципа подстановки Барбары Лисков (LSP)

Service Locator нарушает контракты подтипов, создавая неявные зависимости:

interface LoggerInterface {
    public function log(string $message);
}

class DatabaseLogger implements LoggerInterface {
    public function log(string $message) {
        // Логирует в базу данных
    }
}

class UserService {
    public function register() {
        // Мы получаем абстракцию, но не знаем конкретную реализацию
        $logger = ServiceLocator::get(LoggerInterface::class);
        
        // Нарушение LSP: можем получить любой LoggerInterface,
        // но некоторые реализации могут иметь побочные эффекты
    }
}

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

### 4. Нарушение Принципа разделения интерфейса (ISP)

Service Locator вынуждает клиентов зависеть от ненужных методов:

class ServiceLocator {
    public static function get(string $service) {...}
    public static function set(string $service, object $instance) {...}
    public static function has(string $service): bool {...}
    public static function remove(string $service) {...}
    public static function clear() {...}
}

// Клиент использует только get(), но зависит от всего интерфейса
class ProductService {
    public function update() {
        // Используется только один метод из многих
        $repo = ServiceLocator::get(ProductRepository::class);
    }
}

### 5. Нарушение Принципа инверсии зависимостей (DIP)

Это наиболее серьезное нарушение — зависимость от конкретики вместо абстракций:

class OrderProcessor {
    public function process() {
        // Прямая зависимость от конкретного класса ServiceLocator
        $notifier = ServiceLocator::get(Notifier::class);
        $payment = ServiceLocator::get(PaymentGateway::class);
        
        // Нарушение DIP: высокоуровневый модуль зависит от низкоуровневого
    }
}

DIP требует, чтобы:

  1. Модули высокого уровня не зависели от модулей низкого уровня
  2. Оба зависели от абстракций

Service Locator создает скрытые зависимости, которые:

  • Усложняют тестирование — мокирование зависимостей требует настройки глобального состояния
  • Снижают читаемость — зависимости неявные, их трудно отследить
  • Создают хрупкость — изменения в конфигурации локатора ломают удаленный код
  • Провоцируют циклические зависимости — сервисы могут косвенно зависеть друг от друга через локатор

### Альтернатива: Внедрение зависимостей (Dependency Injection)

// Соблюдение SOLID через Constructor Injection
class UserService {
    private Validator $validator;
    private Mailer $mailer;
    private LoggerInterface $logger;
    
    // Зависимости явно объявлены и внедрены
    public function __construct(
        Validator $validator,
        Mailer $mailer,
        LoggerInterface $logger
    ) {
        $this->validator = $validator;
        $this->mailer = $mailer;
        $this->logger = $logger;
    }
    
    public function register(User $user) {
        // Чистая бизнес-логика
        $this->validator->validate($user);
        $this->mailer->sendWelcome($user);
        $this->logger->log('User registered');
    }
}

Итог: Service Locator нарушает все пять принципов SOLID, создавая тесную связь, неявные зависимости и проблемы с поддерживаемостью. В современной PHP-разработке предпочтительно использовать контейнер внедрения зависимостей (DI Container), который применяет паттерн Dependency Injection, сохраняя преимущества централизованного управления зависимостями без недостатков Service Locator.