PassFront
  1. PassFront
  2. База знаний
  3. Порождающие паттерны

Чем DI-контейнер отличается от Service Locator?

Порождающие паттерны

Оба отделяют создание зависимостей от использования, но DI-контейнер сам передаёт зависимости в класс (push), а через Service Locator класс запрашивает их сам (pull) — и тем самым прячет, что ему нужно.

DI-контейнер

  • Регистрация: токен или интерфейс → реализация + время жизни (singleton, scoped, transient).
  • Контейнер строит граф и внедряет зависимости через конструктор; класс о контейнере не знает.
  • Примеры: NestJS, Angular, InversifyJS.

Service Locator

  • Глобальный реестр, у которого класс внутри методов просит locator.get('clock').
  • Зависимости не видны в сигнатуре, чтобы их узнать, надо читать тело класса.
  • Незарегистрированный сервис всплывает в рантайме в момент вызова, а не при сборке графа на старте.
  • В тестах подменяют глобальное состояние вместо передачи мока в конструктор.
/** Контракт Clock: { now(): Date } */
// DI: зависимость видна в конструкторе
class InvoiceService {
  #clock;
  constructor(clock) { this.#clock = clock; }
  issue() { return { date: this.#clock.now() }; }
}
// Service Locator: зависимость спрятана внутри
const registry = new Map();
class HiddenInvoiceService {
  issue() {
    const clock = registry.get('clock');
    return { date: clock.now() };
  }
}

Где локатор допустим

В composition root и инфраструктуре фреймворка: контейнер по сути и есть локатор, беда начинается, когда его вызывает бизнес-код.

Коротко для собеседования

Разница в направлении: при DI зависимости приходят в конструктор и видны в сигнатуре, а локатор класс опрашивает сам внутри методов. Локатор допустим в composition root, но в бизнес-коде он прячет зависимости и переносит ошибки в рантайм; для небольших проектов хватает pure DI без контейнера.

Другие вопросы раздела