Dependency Inversion Principle, DIP - Принцип инверсии зависимостей

Принцип инверсии зависимостей (Dependency Inversion Principle) служит для создания слабосвязанных сущностей, которые легко тестировать, модифицировать и обновлять. Этот принцип можно сформулировать следующим образом:

Зависимости внутри системы строятся на основе абстракций.
Модули верхних уровней не должны зависеть от модулей нижних уровней. Оба типа модулей должны зависеть от абстракций.
Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций.

Или зависимости должны строится относительно абстракций, а не деталей.

husband.php

class lowRankingMale {
    public function eat() {
        $wife = new Wife();
        $food = $wife->getFood();
        // ... eat
    }
}

wife.php

class Wife {
    public function getFood() {
        // ...
        return $food;
    }
}

В данном пример класс lowRankingMale жёстко зависит от класса Wife. Если нам понадобится в классе lowRankingMale поменять Wife на другой класс потомок - то получится весьма негибко. Кроме того будет нарушен принцип открытости закрытости.

Улучшим наш пример.

class averageRankingMale {
 
    private $wife;
 
    public function __construct(Wife $wife) {
        $this->wife = $wife;
    }
 
    public function eat() {
        $food = $this->wife->getFood();
        // ... eat
    }
}

Эта система уже лучше. Она сама не создаёт объекты класса Wife а принимает его в конструкторе. Тем не менее у нас остаётся жёсткая зависимость от класса Wife.

Окончательно улчшим наш пример:

class highRankingMale {
 
    private $foodProvider;
 
    public function __construct(IFoodProvider $foodProvider) {
        $this->foodProvider = $foodProvider;
    }
 
    public function eat() {
        $food = $this->foodProvider->getFood();
        // ... eat
    }
}
interface IFoodProvider {
    public function getFood();
}
class Restaurant implements IFoodProvider {
    public function getFood() {
        // ...
        return $food;
    }
}
class Wife implements IFoodProvider{
    public function getFood() {
        // ...
        return $food;
    }
}

В данном примере highRankingMale  не привязан ни к какому конкретному классу. Он использует интерфейс IFoodProvider, который обеспечивает возможность выбора.