Поиск…


Вступление

Инъекция зависимостей (DI) - это причудливый термин для «передачи вещей в» . Все это действительно означает передачу зависимостей объекта через конструктор и / или сеттеры вместо их создания при создании объекта внутри объекта. Инъекция зависимостей также может относиться к контейнерам для инъекций зависимостей, которые автоматизируют конструкцию и инъекцию.

Инъекция конструктора

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

В следующем примере Component будет зависеть от экземпляра Logger , но он не создает его. Это требует, чтобы один был передан как аргумент конструктору.

interface Logger {
    public function log(string $message);
}

class Component {
    private $logger;

    public function __construct(Logger $logger) {
        $this->logger = $logger;
    }
}

Без инъекции зависимостей код, вероятно, будет похож на:

class Component {
    private $logger;

    public function __construct() {
        $this->logger = new FooLogger();
    }
}

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

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

Впрыск сеттера

Зависимости также могут быть введены сеттерами.

interface Logger {
    public function log($message);
}

class Component {
    private $logger;
    private $databaseConnection;

    public function __construct(DatabaseConnection $databaseConnection) {
        $this->databaseConnection = $databaseConnection;
    }

    public function setLogger(Logger $logger) {
        $this->logger = $logger;
    }

    public function core() {
        $this->logSave();    
        return $this->databaseConnection->save($this);
    }

    public function logSave() {
         if ($this->logger) {
            $this->logger->log('saving');
        }
    }
}

Это особенно интересно, когда основные функции класса не зависят от зависимости от работы.

Здесь единственной необходимой зависимостью является DatabaseConnection поэтому она находится в конструкторе. Зависимость Logger является необязательной и, следовательно, не обязательно должна быть частью конструктора, что делает класс более удобным в использовании.

Обратите внимание, что при использовании инсталляции сеттера лучше расширить функциональность, а не заменять ее. При настройке зависимости нет ничего, подтверждающего, что зависимость не изменится в какой-то момент, что может привести к неожиданным результатам. Например, FileLogger можно установить FileLogger , а затем установить MailLogger . Это разрушает инкапсуляцию и затрудняет поиск журналов, потому что мы заменяем зависимость.

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

interface Logger {
    public function log($message);
}

class Component {
    private $loggers = array();
    private $databaseConnection;

    public function __construct(DatabaseConnection $databaseConnection) {
        $this->databaseConnection = $databaseConnection;
    }

    public function addLogger(Logger $logger) {
        $this->loggers[] = $logger;
    }

    public function core() {
        $this->logSave();
        return $this->databaseConnection->save($this);
    }

    public function logSave() {
        foreach ($this->loggers as $logger) {
            $logger->log('saving');
        }
    }
}

Подобно этому, всякий раз, когда мы будем использовать основные функции, он не будет ломаться, даже если добавлена ​​зависимость от журнала, и любой добавленный регистратор будет использоваться, даже если бы был добавлен другой регистратор. Мы расширяем функциональность, а не заменяем ее.

Контейнерная инъекция

Инъекция зависимостей (DI) в контексте использования контейнера для инъекций зависимостей (DIC) можно рассматривать как надмножество инъекции конструктора. DIC, как правило, анализирует свойства типа конструктора класса и устраняет его потребности, эффективно вводя зависимости, необходимые для выполнения экземпляра.

Точная реализация выходит далеко за рамки этого документа, но в ее основе DIC полагается на использование подписи класса ...

namespace Documentation;

class Example
{
    private $meaning;

    public function __construct(Meaning $meaning)
    {
        $this->meaning = $meaning;
    }
}

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

// older PHP versions
$container->make('Documentation\Example');

// since PHP 5.5
$container->make(\Documentation\Example::class);

Если вы используете PHP в версии не менее 5.5 и хотите получить имя класса таким образом, как показано выше, правильным способом является второй подход. Таким образом, вы можете быстро найти применение класса с использованием современных IDE, что значительно поможет вам с возможностью рефакторинга. Вы не хотите полагаться на обычные строки.

В этом случае Documentation\Example знает, что ему нужно Meaning , а DIC, в свою очередь, создает экземпляр типа Meaning . Конкретная реализация не должна зависеть от экземпляра потребления.

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

Это имеет ряд преимуществ, поскольку DIC может

  • Совместное использование общих экземпляров
  • Предоставить фабрике разрешение на подпись типа
  • Разрешить подпись интерфейса

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



Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow