Что такое Dependency Injection?

Теория: https://maxsite.org/page/php-di

Принцип инверсии зависимостей или Dependency inversion principle (DIP) — принцип IoC с уклоном в сторону ООП. Он состоит из двух формулировок:

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

  • Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций.

Смысл DIP сводится к тому, чтобы уменьшить зависимость между объектами с помощью абстрактных классов или интерфейсов. И это уже можно выразить в коде. Рассмотрим пример.

class Connect {
    ...
}
 
class DB {
     private $con;
  
     public function __construct(Connect $connection) {
         $this->con = $connection;
     }
}

Класс DB имеет жесткую связь с классом Connect, что как раз и нарушает принцип DIP. Поэтому следует выделить абстракцию более высокого уровня и связать классы через него. Например можно создать интерфейс.

interface IConnect {
    ...
}
 
class Connect implements IConnect {
    ...
}
 
class DB {
     private $con;
 
     public function __construct(IConnect $connection) {
         $this->con = $connection;
     }
}

Класс DB теперь завязан не на конкретную реализацию Connect, а на интерфейс IConnect.

То есть прямой связи между Connect и DB больше нет.

В качестве примера паттерна, где отсутствует DIP можно привести Абстрактную фабрику.

Dependency injection (внедрение зависимости)

Обратите внимание, что класс DB всё равно зависит от другого класса. Когда мы инстанцируем DB, то необходимо передать в его конструктор другой объект. Вот это, собственно, и есть внедрение зависимости в «чистом» виде.

$connect = new Connect();
$db = new DB($connect);
 
$db->...;

Практика: см. https://www.kobzarev.com/programming/di/ — разберем на примере:

Допустим, есть исходный класс для рефакторинга:

<?php
 
namespace PluginName;
 
class Order {
  public $id;
}
 
class OrderProcessing {
  public function create_new_order() {
    /* Логика выполнения заказа */
    $this->log( 'Order created!' );
  }
  private function log( string $message ) {
    echo "Save log with message: {$message}" . PHP_EOL;
  }
}
 
$order_processing = new OrderProcessing();
$order_processing->create_new_order();

Первая проблема, которую вы можете увидеть, — это принцип единой ответственности. В OrderProcessing мы используем логику класса Logger. Перенесем эту логику в отдельный класс.

<?php
 
namespace PluginName;
 
class Logger {
  public function log( string $message ) {
    echo "Save log with message: {$message}" . PHP_EOL;
  }
}
 
class OrderProcessing {
  public function create_new_order() {
    /* Логика выполнения заказа */
    $logger = new Logger();
    $logger->log('Order created!');
  }
}
 
$order_processing = new OrderProcessing();
$order_processing->create_new_order();

Небольшой рефакторинг, и мы можем повторно использовать класс Logger в разных частях нашего приложения. Но мы не можем так легко изменить хранилище Logger.

Для понимания зависимостей класса OrderProcessing необходимо прочитать весь класс. Каждая внешняя функция или класс, который используется внутри нашего класса, называется зависимостями.

Некоторые зависимости хороши, например, некоторая функция PHP, которая очищает какое-то свойство. Но в нашем примере у нас есть плохая зависимость new Logger, которая называется hard dependency (жесткая зависимость).

Почему жесткая зависимость — это плохо? Потому что мы не можем изменить логику без изменения этих классов. Если я хочу изменить хранилище записей журнала, мне нужно изменить оба класса.

Как сделать наш пример более гибким? Конечно, используйте шаблон проектирования внедрения зависимостей.

Встречайте Dependency Injection

(звезда) Лучше всего использовать интерфейс в конструкторе вместо Logger, но об этом позже.

<?php
 
namespace PluginName;
 
class OrderProcessing {
  private $logger;
  public function __construct( Logger $loger ) {
    $this->logger = $logger;
  }
  public function create_new_order() {
    /* Some kind of logic for create order */
    $this->logger->log('Order created!');
  }
}
 
$logger           = new Logger();
$order_processing = new OrderProcessing( $logger );
$order_processing->create_new_order();
Tags

Нет Ответов

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Этот сайт использует Akismet для борьбы со спамом. Узнайте, как обрабатываются ваши данные комментариев.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Этот сайт использует Akismet для борьбы со спамом. Узнайте, как обрабатываются ваши данные комментариев.

Рубрики


Подпишись на новости
👋

Есть вопросы?