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

Что такое DIP?

2.3 Middle🔥 171 комментариев
#Архитектура и паттерны

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

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

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

DIP (Dependency Inversion Principle) — Принцип Инверсии Зависимостей

DIP — один из пяти принципов SOLID, который гласит: зависимости должны быть от абстракций (интерфейсов), а не от конкретных реализаций. Высокоуровневые модули не должны зависеть от низкоуровневых модулей; оба должны зависеть от абстракций.

Основная идея

// ❌ Нарушение DIP — высокий уровень зависит от низкого
class OrderService {
    private MySQLDatabase $db; // Зависит от конкретной БД!
    
    public function __construct() {
        $this->db = new MySQLDatabase();
    }
    
    public function createOrder($data) {
        return $this->db->insert('orders', $data);
    }
}

// Проблемы:
// - Сложно менять БД (MySQL → PostgreSQL → MongoDB)
// - Сложно тестировать (нужна реальная БД)
// - Класс делает слишком много (создаёт зависимость сам)

Правильное решение — инверсия зависимостей:

// ✅ Соблюдение DIP — зависимость от интерфейса
interface DatabaseInterface {
    public function insert(string $table, array $data);
    public function find(string $table, int $id);
}

class MySQLDatabase implements DatabaseInterface {
    public function insert(string $table, array $data) {
        // MySQL реализация
    }
    
    public function find(string $table, int $id) {
        // MySQL реализация
    }
}

class OrderService {
    private DatabaseInterface $db; // Зависит от интерфейса!
    
    public function __construct(DatabaseInterface $db) {
        $this->db = $db;
    }
    
    public function createOrder($data) {
        return $this->db->insert('orders', $data);
    }
}

Основные правила DIP

  • Высокоуровневые модули должны зависеть от абстракций — заказ зависит от интерфейса БД, не от MySQL
  • Низкоуровневые модули тоже от абстракций — MySQL реализует интерфейс БД
  • Абстракции не зависят от деталей — интерфейс не меняется, когда меняется реализация

Внедрение зависимостей (Dependency Injection)

// ✅ Инъекция через конструктор (лучший способ)
class UserService {
    public function __construct(
        private DatabaseInterface $db,
        private EmailInterface $email,
        private LoggerInterface $logger
    ) {}
    
    public function register(string $email, string $password) {
        try {
            $user = $this->db->insert('users', [
                'email' => $email,
                'password' => bcrypt($password)
            ]);
            
            $this->email->send($email, 'Welcome!');
            $this->logger->log('User registered');
            return $user;
        } catch (Exception $e) {
            $this->logger->log('Registration failed');
            throw $e;
        }
    }
}

Контейнер зависимостей

// ✅ Централизованное управление зависимостями
class Container {
    private array $bindings = [];
    
    public function bind(string $key, $value) {
        $this->bindings[$key] = $value;
    }
    
    public function get(string $key) {
        if (isset($this->bindings[$key])) {
            $binding = $this->bindings[$key];
            return is_callable($binding) ? $binding($this) : $binding;
        }
        throw new Exception('Binding not found');
    }
}

// Регистрируем зависимости
$container = new Container();
$container->bind('db', fn($c) => new MySQLDatabase());
$container->bind('email', fn($c) => new SMTPEmail());
$container->bind('logger', fn($c) => new FileLogger());
$container->bind('userService', fn($c) => new UserService(
    $c->get('db'),
    $c->get('email'),
    $c->get('logger')
));

DIP в архитектуре (Domain-Driven Design)

// ✅ Правильная иерархия зависимостей
namespace App\Domain;

interface UserRepository {
    public function findById(int $id): ?User;
    public function save(User $user): void;
}

interface EmailService {
    public function send(string $to, string $subject, string $body): void;
}

// Бизнес-логика зависит от интерфейсов
class RegisterUserUseCase {
    public function __construct(
        private UserRepository $users,
        private EmailService $email
    ) {}
    
    public function execute(string $email, string $password): User {
        $user = new User($email, bcrypt($password));
        $this->users->save($user);
        $this->email->send($email, 'Welcome', 'Thanks for signing up!');
        return $user;
    }
}

Преимущества DIP

  • Слабая связанность — модули независимы друг от друга
  • Легче тестировать — можно подменять реализации на mock'и
  • Гибкость — просто меняешь реализацию интерфейса
  • Масштабируемость — легко добавлять новые реализации
  • Чистая архитектура — логика отделена от деталей реализации