Что такое внедрение зависимостей в 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 и узнали, когда его следует использовать.
- 
					Создано 15.08.2017 11:18:06  
- 
					 Михаил Русаков Михаил Русаков
 
			 
			 
		 
				 
			 
				 
				 
				 
				
Комментарии (0):
Для добавления комментариев надо войти в систему.
Если Вы ещё не зарегистрированы на сайте, то сначала зарегистрируйтесь.