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

В объектно-ориентированном программировании часто возникают ситуации, когда объект должен содержать в некую конфигурацию, которую он берет из объекта другого класса.

Например, если у нас есть RSS-агрегатор, который использует кеш, то как нам конфигурировать этот кеш?

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

class FeedFetcher {
 protected $cache;
 function __construct() {
    $this->cache = new FeedCache();
 }
}

Класс кеша может в будущем меняться, требовать каких-то новых параметров в конструкторе, так что наше «жесткое связывание» пагубно скажется на поддержке кода. К тому же, возникает вопрос, как нам конфигурировать кеш. Все опции: делать это в FeedFetcher, делать это вне FeedFetcher, либо расширять FeedFetcher, чтобы он кофигурировал кеш, ведут к плохо поддерживаемому коду. В первом случае при любом изменении класса кеша придется вносить изменения в FeedFetcher, во втором случае FeedFetcher будет зависеть от каких-то внешних констант/настроек, а в третьем он замусорится вещами, относязимися к другому классу (кеша).

Лучшим выходом в подобных ситуациях является паттерн внедрения зависимости. Вместо создания объекта другого класса в конструкторе лучше передавать этот объект в параметрах конструктора, вот так:

class FeedFetcher {
 protected $cache;
 function __construct(Cache $cache) {
   $this->cache = $cache;
 }
}

При таком подходе конфигурирование обоих классов будет осуществляться снаружи, в клиенте, который их использует:

$cache = new FileCache(‘/tmp’);
$fetcher = new FeedFetcher($cache);

В целом, внедрение зависимости можно проводить через конструктор (Constructor injection), как в вышеуказанном примере, через сеттер (Setter injection):

class FeedFetcher
    {
      function setCache(Cache $cache)
      {
        $this->cache = $cache;
      }
    }

и через свойство (Property injection):

class FeedFetcher
{
   public $cache;
}

$user->cache= $cache;

Обычно для обязательных зависимостей (тех, которые всегда должны присутствовать) используется инъекция через конструктор, а для опциональных — через сеттер.

Все современные фреймворки используют этот нехитрый паттерн проектирования.

Оставить комментарий

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

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