Принцип инверсии зависимостей

Вступление

Принцип инверсии зависимостей относится к особой форме развязки программных модулей. При следовании этому принципу обычные отношения зависимости, устанавливаемые от высокоуровневых модулей установки политики к низкоуровневым модулям зависимостей, меняются местами, что делает модули высокого уровня независимыми от деталей реализации низкоуровневого модуля. Принцип гласит:

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

Требуя, чтобы как высокоуровневые, так и низкоуровневые объекты зависели от одной и той же абстракции, этот принцип проектирования меняет представление некоторых людей об объектно-ориентированном программировании.

На самом деле, вместо того, чтобы говорить, что для метода требуется конкретный класс VideoPlayer, он должен требовать объект, который придерживается VideoPlayerInterface. Таким образом, мы можем передать несколько версий Video Player в один и тот же метод, и он будет работать одинаково.

Практический пример (ы)

Нарушение DIP

Ниже приведен пример класса SendWelcomeMessage, который принимает экземпляр класса Mailer в своем конструкторе как внедренную зависимость. Это нарушение принципа инверсии зависимостей, поскольку метод полагается на конкрецию, а не на абстракцию.

class Mailer
{
    // Methods for a Mailer class
}
class SendWelcomeMessage
{
    private $mailer;
    public function __construct(Mailer $mailer)
    {
        $this->mailer = $mailer;
    }
}

Соблюдение DIP

Чтобы провести рефакторинг этого кода в соответствии с принципом инверсии зависимостей, вам нужно будет извлечь общие методы, составляющие класс Mailer. Для простоты мы извлечем метод под названием send, чтобы каждый экземпляр MailerInterface comtract мог что-то отправлять.

interface MailerInterface
{
    public function send();
}
class SmtpMailer implements MailerInterface
{
    public function send()
    {
        // Send an email via SMTP
    }
}
class SendSlackMailer implements MailerInterface
{
    public function send()
    {
        // Send a message via Slack
    }
}
class SendWelcomeMessage
{
    private $mailer;
    public function __construct(MailerInterface $mailer)
    {
        $this->mailer = $mailer;
    }
}

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

Заключение

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