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

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

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

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

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
class FeedFetcher {
protected $cache;
function __construct() {
$this->cache = new FeedCache();
}
}
class FeedFetcher { protected $cache; function __construct() { $this->cache = new FeedCache(); } }
class FeedFetcher {
 protected $cache;
 function __construct() {
    $this->cache = new FeedCache();
 }
}

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

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

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
class FeedFetcher {
protected $cache;
function __construct(Cache $cache) {
$this->cache = $cache;
}
}
class FeedFetcher { protected $cache; function __construct(Cache $cache) { $this->cache = $cache; } }
class FeedFetcher {
 protected $cache;
 function __construct(Cache $cache) {
   $this->cache = $cache;
 }
}

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

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
$cache = new FileCache(‘/tmp’);
$fetcher = new FeedFetcher($cache);
$cache = new FileCache(‘/tmp’); $fetcher = new FeedFetcher($cache);
$cache = new FileCache(‘/tmp’);
$fetcher = new FeedFetcher($cache);

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

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
class FeedFetcher
{
function setCache(Cache $cache)
{
$this->cache = $cache;
}
}
class FeedFetcher { function setCache(Cache $cache) { $this->cache = $cache; } }
class FeedFetcher
    {
      function setCache(Cache $cache)
      {
        $this->cache = $cache;
      }
    }

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

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
class FeedFetcher
{
public $cache;
}
$user->cache= $cache;
class FeedFetcher { public $cache; } $user->cache= $cache;
class FeedFetcher
{
   public $cache;
}

$user->cache= $cache;

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

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

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

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

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