В объектно-ориентированном программировании часто возникают ситуации, когда объект должен содержать в некую конфигурацию, которую он берет из объекта другого класса.
Например, если у нас есть 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;
Обычно для обязательных зависимостей (тех, которые всегда должны присутствовать) используется инъекция через конструктор, а для опциональных — через сеттер.
Все современные фреймворки используют этот нехитрый паттерн проектирования.