Используем инъекцию зависимостей в PHP

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

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

Это фундаментальное требование означает, что использовать значения (сервисы), вызванные в пределах класса от новых или статических методов запрещено. Класс должен принять значения, переданные извне. Как вы увидите в разделах этой статьи, существует три распространенных способа принять инъекцию зависимостей для клиента: через конструктор, через метод и инъекции на основе интерфейса. Наиболее распространенный способ — инъекция через конструктор, но через метод и через конструктор принципиально отличаются по времени, за которое могут быть использованы. Инъекция через интерфейс отличается тем что, зависимость контролирует свое внедрение.

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

В качестве простого примера конкретной зависимости являются следующие строки кода:

<?php

class A
{
    public function f_a() {
        $b = new B();
        $b->f_b();
    }
}
?>

<?php

class B
{
    public function f_b() {
        $c = new C();
        $c->f_c();
    }
}
?>

<?php

class C
{
public function f_c();
}

?>

Вы заметите, что класс А зависит от класса B, который зависит от класса C.  Кроме того, каждый раз, когда мы прилагаем код создания объекта внутри класса, мы делаем конкретную зависимость к этому классу. Конкретные зависимости являются препятствием для написания тестируемого кода. Лучший способ состоит в создании объекта класса B в классе А. Эти объекты могут быть предоставлены через конструктор или метод сеттер.

class Car {

 // property declaration
 private $maximumSpeed = 200;
 
 // method declaration
 public function getMaximumSpeed() {
 	return $this->maximumSpeed;
 }
}

$myCar = new Car();
echo $myCar->getMaximumSpeed();


class Key {
 private $car;
 public function __construct(Car $car) {
 	$this->car = $car;
 }
 public function TurnKey() {
 if (/*condition */) {
 	$this->Car->turnOn();
 }
 }
}

Примечание: В старые времена, это было бы сделано таким образом, но инъекцию зависимости именно так точно не делали:

class Key {
  private $car;
  public function __construct() {
    $this-> = new Car();
  }
  //...
}

 

Виды инъекции

Существует множество видов инъекций, а также вы увидите далее в этой статье несколько простых примеров:

Конструктор (Constructor Injection)

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

  • Зависимость не может быть изменена или модифицирована в течении продолжительности жизни объекта, учитывая, что конструктор вызывается только тогда, когда создается наш объект
  • С помощью инъекции конструктора гарантируется, что требуемые зависимости присутствуют, в случае, если это зависимость необходима классу и не может работать без него

Далее приведен простой пример использования инъекции конструктора:

class Key implements ModelCar{}
class Car
{
  private $modelCar;
  public	function __construct(ModelCar $ modelCar)
    {
      $this-> modelCar	=	$ modelCar;
    }
}
$key = new Key('Citroen	C4');
$car = new Car($key);

Инъекция через метод класса (Setter Injection)

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

class ModelCar implements Color{}
class Car
{
  private $color;
  public	function setColor(Color $color)
    {
      $this->color	=	$color;
    }
}
$color = new ModelCar('Citroen C4');
$car = new Car();
$car->setColor($color);

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

  • Допускают дополнительные зависимости и класс может быть создан со значениями по умолчанию, если вам не нужна зависимость, то просто не вызываем метод класса
  • Добавление новых зависимостей — несложная работа, все что вам нужно сделать — это добавить новый метод сеттер, который не нарушит существующий код
  • Вы можете вызывать метод сеттер несколько раз

Внедрение в свойство (Property Injection)

Другой способ использования инъекции зависимости заключается в установке открытый полей непосредственно в классе, но есть некоторые недостатки, поэтому могут возникнуть некоторые проблемы:

  • Вы не можете быть уверены, что зависимость внедрена, пока не написали код внутри класса для тестирования класса перед его использованием
  • Вы не можете контролировать, когда зависимость внедрена вообще, она может быть изменена в любой момент времени существования объекта

Но, полезно знать, что это может быть сделано с использованием сервис-контейнера, прежде всего, если вы работаете с кодом, который вы не можете контролировать, например в сторонней библиотеке, которая использует общие свойства с этой зависимостью.

Простой пример использования этого типа инъекций приведен ниже:

class Car
{
  public $trailer;
}

$car = new Car();
$car->trailer = new Trailer();

Инъекция через интерфейс внедрения (Interface Injection)

Интерфейс внедрения отличается от других тем, что зависимость может контролировать свои инъекции. Все, что нужно, это отделить код конструкции (инъектор), чтобы взять на себя ответственность за представление клиента и его зависимостей друг другу. Преимущество такой инъекции состоит в том, что зависимости могут быть совершенно не знать своих клиентов, кроме того еще можно получить ссылку на нового клиента и используя его, отправить обратную ссылку назад клиенту. Таким образом, зависимости становятся инъекторами. Разгадка заключается в том, что способ внедрения (это может быть классический метод сеттер) осуществляется через интерфейс.
Далее, простой пример на PHP, который использует механизм инъекции через интерфейса:

class Car{
  protected $carModel;
  function _construct(CarModelInterface){
    $this-> carModel =$ carModel;
  }

interface CarModelInterface {
  function get($key);
  function set($key,$value);
}

Отражения

Отражения являются способностью к самоанализу и перепроектированию функций, классов, методов и интерфейсов во время выполнения. Это позволяет найти конкретную информацию о вашем коде, например внутренние классы, свойства, методы и даже DOC блоки для этих методов. В приведенных ниже строках кода, показан простой пример инъекции зависимостей, который использует механизм отражения:

class Car
{
  private $attachTrailer;
}
$car = new Car();
$trailer = new ReflectionClass($car);
$attachTrailer = $trailer->getProperty('attachTrailer');
$attachTrailer ->setAccessible(true);
$attachTrailer ->setValue($car, new AttachTrailer());

Итак, как мы уже видели до сих пор, какие есть полезные методы для осуществления инъекции зависимостей в PHP, таким образом, мы можем сделать вывод, основные аспекты внедрения зависимостей в список преимуществ, которые перечислены в следующих строках:

Преимущества использования инъекции зависимостей

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

Как главный недостаток инъекции зависимостей:

Комплексная инициализация инъекции зависимостей

Правильное решение для решения вышеуказанного недостатка использовать контейнер инъекции зависимостей. В следующем разделе этой статьи еще представлены контейнеры зависимости инъекций, используемых в работе с PHP и некоторые примеры использования этих.

Контейнер внедрения зависимости

Контейнер внедрения зависимости представляет собой РНР объект, который обрабатывает создание экземпляров других объектов, другими словами, инъекции зависимостей контролируют процесс создания объекта; внедрение и введение каких-либо зависимостей перед возвращением экземпляра к вызывающему объекту.

В следующем разделе этой статьи будут представлены некоторые из наиболее часто используемых контейнеров для инъекций PHP зависимостей (или рамки):

Pimple: Простой контейнер внедрения зависимости, который имеет преимущество замыкания в PHP для определения зависимостей управляемым способом. Здесь приведен простой пример использования этого контейнера:

<?php

// create an instance of Pimple to act as a container for storing dependencies
$container = new Pimple();
  
// define a key that holds the class name
$container['className'] = 'Car';

// define a closure to return the instance of the specified class that acts as a service
$container['objectName'] = function ($car) {
    return new $car['className']();
};

$example = $container['objectName'];

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

Dice: Еще один простой контейнер инъекции зависимостей, используемый в PHP является Dice, один 100-строчный класс, что позволяет разработчикам перемещать объект создания логики из логики их приложения. Dice был разработан, чтобы минимизировать и упростить код приложения, что делает жизнь разработчиков приложений проще.

Orno: быстрый и мощный контейнер зависимостей инъекций, который позволяет автоматическое разрешение зависимостей. Orno \ Di является небольшим, но мощным контейнером зависимостей инъекций, который позволяет отделить компоненты в приложении, чтобы написать чистый и тестируемый код. Контейнер может автоматически разрешать зависимости объектов проходящих через него.

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

PHP-DI: Популярный контейнер внедрения зависимостей для PHP, который стремится быть практичным и мощным.

Zend \ DI: Контейнер внедрения зависимостей предусмотрен в Zend Framework и его основная задача заключается в предоставлении вам экземпляров запрашиваемого объекта, заполненного всеми зависимостями, необходимых для корректной работы.

Symfony2: Контейнер внедрения зависимостей обеспечивается в рамках Symfony. Symfony2 использует очень надежный компонент DI, который основан на Java Spring. Контейнеры внедрения зависимостей (или фреймворки) управляют процессом создания объекта; внедрения и введение каких-либо зависимостей перед возвращением экземпляра к вызывающему объекту.

Вывод

В этой статье вы узнали о Dependency Injection и основных PHP контейнеров / концепций, используемых в работе с PHP, и мы можем сделать вывод о том, что инъекция зависимостей может сделать жизнь разработчика гораздо проще.


Перевод оригинальной статьи: Using Dependency Injection in PHP by Octavia Andreea Anghel
Переведенная статья не исключает возможных ошибок!

Написать комментарий

Ваш Email не будет опубликован

На нашем сервере не хранятся какие-либо объекты авторского права согласно действующему законодательству страны, в которой находится сайт и сервер. Все материалы хранятся на файлообменных общедоступных серверах и у нас представлены только ссылки, что не является нарушением законодательства текущей страны местонахождения сайта. Если вы хотите заявить о нарушении авторских прав, пожалуйста, предоставьте нам полную информацию и обоснованные аргументы, согласно которым мы должны удалить какую-либо информацию с нашего сайта.