Что такое внедрение зависимостей в PHP
Разработка любого, мало-мальски сложного, Web-приложения редко когда обходиться без использования сторонних компонентов и библиотек. При этом возникает вопрос - как управлять зависимостями приложения? Управление зависимостями в PHP является одной из важнейших и сложнейших задач при написании Web-приложений. Оно используется для грамотной организации процесса разработки, избавляя программиста от большого числа рутинных задач.
При написании приложения, как правило, используется множество разнообразных PHP классов и один класс может вызывать методы одного или нескольких других классов. Поэтому мы говорим, что данный класс зависит от других классов. Например:
<?php
// класс телефона
class Phone
{
// делает звонок
public function makeCall() {
// используя встроенный передатчик
$radio = new RadioSignaler();
$radio -> connect();
}
}
?>
Здесь класс Phone зависит от класса RadioSignaler, и, если по какой-то причине класс RadioSignaler окажется недоступен, то данный код работать не будет. Более того, всякий раз, когда создание экземпляра одного класса прописывается в другом классе, то создается жесткая зависимость от данного класса, что делает крайне сложным написание тестируемого и легкого в сопровождении кода.
Перед тем как двигаться дальше, давайте рассмотрим более реалистичный пример. Как-то я уже писал как создать социальную сеть, а в социальных сетях крайне популярно обмениваться контентом (новости, фото, видео). Так вот, представьте, что у нас есть класс под названием SocialFeeds, который собирает новости из различных источников, таких как ВКонтакте, Twitter, Facebook и т. д. Для каждого из этих источников есть свой отдельный класс сервиса. Мы рассмотрим класс TwitterService.
Класс SocialFeeds отправляет запрос на Twitter при помощи класса TwitterService. Этот класс, в свою очередь, извлекает из базы данных специальный токен (ключ) пользователя для доступа к API Twitter. Токен передается классу OAuth, который, используя полученый ключ, запрашивает новости из Twitter и возвращает их обратно в класс SocialFeeds.
<?php
class SocialFeeds
{
// получает новости из Twitter и печатает их
public function getSocialFeeds() {
$twitSrv = new TwitterService();
print $twitSrv -> getTweets();
}
}
// класс
class TwitterService
{
// получает твиты
public function getTweets() {
// выборка токена из базы данных
$db = new DB();
$query = "SELECT token FROM api";
$token = $db -> makeQuery($query);
// авторизация посредством OAuth
$oauth = new OAuth();
return $oauth -> getTweetsFeed($token);
}
}
class OAuth
{
public function getTweetsFeed( $token ) {
// возвращает твиты, используя токен
}
}
class DB
{
public function makeQuery() {
// Возвращает токен из базы данных
}
}
?>
Здесь класс SocialFeeds зависит от класса TwitterService, который, в свою очередь, зависит от классов DB и OAuth, таким образом, и класс SocialFeeds косвенно зависит от классов DB и OAuth.
Так в чем же проблема? А проблема в том, что класс SocialFeeds зависит от конкретной реализации этих трех классов, делая невозможным, например, тестирование класса SocialFeeds как отдельную единицу. К тому же, если нам нужно будет использовать другую базу данных или провайдера OAuth, то придется искать и менять эти классы по всему коду, в котором они встречаются.
Вот здесь и становится очевидным необходимость использования механизма внедрения зависимостей (Dependency Injection, DI) в PHP. C помощью него, мы можем создавать объекты динамически, только тогда, когда это необходимо. Существуют две техники реализации этого механизма:
1) Инъекция зависимостей посредством конструктора класса. В этом приеме, зависимости объекта создаются извне и передаются конструктору класса в качестве параметра. Вот код:
<?php
class SocialFeeds
{
public $tweetService;
// в качестве парметра передается сервис Twitter
public function __construct( TwitterService $twitService ) {
$this -> twitService = $twitService;
}
// возвращает новости
public function getSocialFeeds() {
print $this -> twitService -> getTweets();
}
}
?>
Экземпляр класса TwitterService передается в качестве параметра в конструктор SocialFeeds. Несмотря на то, что класс SocialFeeds все еще зависит от сервиса TwitterService, теперь у нас есть больше свободы при выборе различных версий провайдера новостей Twitter. Подобное действие применяем и к классам DB и OAuth:
<?php
$db = new DB();
$oauth = new OAuth();
$twSrv = new TwitterService( $db, $oauth );
$sf = new SocialFeeds($twSrv);
$sf -> getSocialFeeds();
?>
2) Инъекция зависимостей через set-метод:
<?php
class SocialFeeds
{
public $tweetService;
// set-метод для Twitter сервиса
public function setTwitterService( TwitterService $tweetService ) {
$this -> tweetService = $tweetService;
}
public function getSocialFeeds() {
print $this -> tweetService -> getTweets();
}
}
// создаем объекты
$db = new DB();
$oauth = new OAuth();
$twSrv = new TwitterService();
$sf = new SocialFeeds();
// устанавливаем зависимости
$twSrv -> setDb($db);
$twSrv -> setOAuth($oauth);
$sf -> setTwitterService($twSrv);
// получаем новостную ленту
$sf -> getSocialFeeds();
?>
Механизм внедрения зависимости в PHP код посредством конструктора класса, или сеттер-метода остается на выбор программиста. Однако существует негласное правило, согласно которому, через конструктор передаются такие зависимости, без которых класс не сможет функционировать, а через set-методы передаются зависимости, которые необязательны.
Таким образом, сегодня мы познакомились с тем, что такое Dependency Injection (DI) в PHP и узнали, когда его следует использовать.
-
- Михаил Русаков
Комментарии (0):
Для добавления комментариев надо войти в систему.
Если Вы ещё не зарегистрированы на сайте, то сначала зарегистрируйтесь.