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

В чем разница между Dependency Inversion и Injection?

2.0 Middle🔥 211 комментариев
#Архитектура и паттерны#ООП

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

🐱
claude-haiku-4.5PrepBro AI28 мар. 2026 г.(ред.)

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

Dependency Inversion vs Dependency Injection

Это часто путают, потому что они связаны, но это разные концепции. Давай разберёмся.

Dependency Injection (DI) — это техника (HOW)

Dependency Injection — способ подачи зависимостей в класс/функцию. Это про механику передачи.

Плохой пример (без DI — жёсткая связь)

class UserService {
  private database: DatabaseConnection;

  constructor() {
    // Сервис создаёт свою зависимость
    // Жёсткая связь, невозможно тестировать
    this.database = new PostgresConnection({
      host: 'localhost',
      port: 5432
    });
  }

  async getUser(id: number) {
    return this.database.query('SELECT * FROM users WHERE id = $1', [id]);
  }
}

// Проблемы:
// 1. Невозможно заменить на другую БД для тестов
// 2. UserService зависит от конкретной реализации PostgresConnection
// 3. Сложно масштабировать

Хороший пример (с DI)

// Интерфейс абстрактизирует зависимость
interface IDatabase {
  query(sql: string, params: any[]): Promise<any>;
}

class UserService {
  constructor(private database: IDatabase) {}

  async getUser(id: number) {
    return this.database.query('SELECT * FROM users WHERE id = $1', [id]);
  }
}

// Теперь можно внедрить разные реализации
const postgresDb = new PostgresConnection();
const userService1 = new UserService(postgresDb);

const mockDb = new MockDatabase();
const userService2 = new UserService(mockDb);

3 способа внедрения зависимостей

1. Constructor Injection (самый распространённый)
class UserService {
  constructor(private database: IDatabase) {}
}

const service = new UserService(new PostgresConnection());
2. Property Injection
class UserService {
  database: IDatabase;
}

const service = new UserService();
service.database = new PostgresConnection();
3. Method Injection
class UserService {
  getUser(id: number, database: IDatabase) {
    return database.query('SELECT * FROM users WHERE id = $1', [id]);
  }
}

const service = new UserService();
service.getUser(1, new PostgresConnection());

Dependency Inversion (DI) Principle — это принцип SOLID (WHAT)

Dependency Inversion Principle гласит:

Высокоуровневые модули не должны зависеть от низкоуровневых модулей. Оба должны зависеть от абстракций.

Это про архитектуру и дизайн отношений между компонентами.

Нарушение Dependency Inversion

// HIGH-LEVEL модуль
class OrderService {
  // Зависит от LOW-LEVEL модуля напрямую (BAD)
  constructor(private emailService: EmailService) {}

  async completeOrder(orderId: number) {
    // ...
    // ПЛОХО: OrderService знает, что уведомления идут через email
    await this.emailService.send('Order completed!');
  }
}

// LOW-LEVEL модуль
class EmailService {
  async send(message: string) {
    // отправка email
  }
}

// Проблемы:
// 1. Если захотим SMS вместо email — нужно менять OrderService
// 2. OrderService жёстко привязан к EmailService
// 3. Нарушается принцип Open-Closed

Следование Dependency Inversion

// АБСТРАКЦИЯ (интерфейс)
interface INotificationService {
  notify(message: string): Promise<void>;
}

// HIGH-LEVEL модуль зависит от абстракции
class OrderService {
  constructor(private notificationService: INotificationService) {}

  async completeOrder(orderId: number) {
    // ...
    // ХОРОШО: OrderService не знает, как отправляются уведомления
    await this.notificationService.notify('Order completed!');
  }
}

// LOW-LEVEL модули реализуют абстракцию
class EmailNotificationService implements INotificationService {
  async notify(message: string): Promise<void> {
    // отправка email
  }
}

class SmsNotificationService implements INotificationService {
  async notify(message: string): Promise<void> {
    // отправка SMS
  }
}

class SlackNotificationService implements INotificationService {
  async notify(message: string): Promise<void> {
    // отправка в Slack
  }
}

// Использование
const emailService = new EmailNotificationService();
const orderService = new OrderService(emailService);

// Позже просто меняем реализацию
const smsService = new SmsNotificationService();
const orderService2 = new OrderService(smsService);

// OrderService не менялся!

Сравнение в таблице

АспектDependency InjectionDependency Inversion
Что этоТехника/паттернSOLID принцип
Про чтоКАК передавать зависимостиКАКИЕ отношения должны быть
ФокусМеханика передачиАрхитектура и дизайн
УровеньТактическийСтратегический
ВопросКак внедрить?Как спроектировать правильно?
Без неёКод жёстко завязан, сложно тестироватьВысокая связанность, сложно менять

Как они работают вместе

Oни часто используются вместе:

// DEPENDENCY INVERSION PRINCIPLE (DIP) — проектирование
// Мы решили: OrderService не должна знать о конкретной реализации уведомлений
interface INotificationService {
  notify(message: string): Promise<void>;
}

// DEPENDENCY INJECTION — реализация DIP
// Мы передаём INotificationService в конструктор
class OrderService {
  constructor(private notificationService: INotificationService) {
    // Получили зависимость извне (внедрение)
  }
}

// Результат: соблюдаем DIP через применение DI техники

Практический пример в Node.js

До: Нарушение обоих принципов

// Плохо: жёсткая связь, нельзя тестировать
class PaymentProcessor {
  async process(orderId: number, amount: number) {
    const stripeService = new StripeService();
    await stripeService.charge(amount);
  }
}

После: Dependency Inversion + Injection

// Хорошо: чистая архитектура
interface IPaymentGateway {
  charge(amount: number): Promise<void>;
}

class PaymentProcessor {
  constructor(private paymentGateway: IPaymentGateway) {}

  async process(orderId: number, amount: number) {
    await this.paymentGateway.charge(amount);
  }
}

// Реализации
class StripePaymentGateway implements IPaymentGateway {
  async charge(amount: number) { /* ... */ }
}

class PayPalPaymentGateway implements IPaymentGateway {
  async charge(amount: number) { /* ... */ }
}

// Тестирование
class MockPaymentGateway implements IPaymentGateway {
  async charge(amount: number) { /* mock */ }
}

// Использование
const processor = new PaymentProcessor(new StripePaymentGateway());
await processor.process(123, 99.99);

// Тестирование
const mockProcessor = new PaymentProcessor(new MockPaymentGateway());
await mockProcessor.process(123, 99.99);

Контейнеры для Dependency Injection

В больших приложениях используют DI контейнеры:

// TypeScript Decorator-based DI
import { Container, Inject } from 'typedi';

Container.set(IDatabase, new PostgresConnection());
Container.set(IEmailService, new EmailService());

@Service()
class UserService {
  @Inject()
  private database: IDatabase;

  @Inject()
  private emailService: IEmailService;
}

// Или с функциональным подходом
const container = createContainer();
container.register('database', PostgresConnection);
container.register('email', EmailService);

const userService = container.get('userService');

Вывод

  • Dependency Injection — техника "как внедрять зависимости"
  • Dependency Inversion — принцип "как проектировать, чтобы классы не зависели от конкретных реализаций"
  • Они часто используются вместе
  • DI помогает реализовать DIP
  • Вместе они делают код гибким, тестируемым и легко расширяемым