2.6. Внедрение Зависимости (Dependency Injection)

2.6.1. Назначение

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

2.6.2. Использование

Объект DatabaseConfiguration внедряется в DatabaseConnection и последний получает всё, что ему необходимо из переменной $ config. Без DI, конфигурация будет создана непосредственно в Connection, что не очень хорошо для тестирования и расширения Connection, так как связывает эти классы напрямую.

2.6.3. Примеры

  • The Doctrine2 ORM использует Внедрение Зависимости например для конфигурации, которая внедряется в объект Connection. Для целей тестирования, можно легко создать макет объекта конфигурации и внедрить его в объект Connection, подменив оригинальный.

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

2.6.4. Диаграмма UML

Alt DependencyInjection UML Diagram

2.6.5. Код

Вы можете найти этот код на GitHub

DatabaseConfiguration.php

 1<?php
 2
 3declare(strict_types=1);
 4
 5namespace DesignPatterns\Structural\DependencyInjection;
 6
 7class DatabaseConfiguration
 8{
 9    public function __construct(
10        private string $host,
11        private int $port,
12        private string $username,
13        private string $password
14    ) {
15    }
16
17    public function getHost(): string
18    {
19        return $this->host;
20    }
21
22    public function getPort(): int
23    {
24        return $this->port;
25    }
26
27    public function getUsername(): string
28    {
29        return $this->username;
30    }
31
32    public function getPassword(): string
33    {
34        return $this->password;
35    }
36}

DatabaseConnection.php

 1<?php
 2
 3declare(strict_types=1);
 4
 5namespace DesignPatterns\Structural\DependencyInjection;
 6
 7class DatabaseConnection
 8{
 9    public function __construct(private DatabaseConfiguration $configuration)
10    {
11    }
12
13    public function getDsn(): string
14    {
15        // this is just for the sake of demonstration, not a real DSN
16        // notice that only the injected config is used here, so there is
17        // a real separation of concerns here
18
19        return sprintf(
20            '%s:%s@%s:%d',
21            $this->configuration->getUsername(),
22            $this->configuration->getPassword(),
23            $this->configuration->getHost(),
24            $this->configuration->getPort()
25        );
26    }
27}

2.6.6. Тест

Tests/DependencyInjectionTest.php

 1<?php
 2
 3declare(strict_types=1);
 4
 5namespace DesignPatterns\Structural\DependencyInjection\Tests;
 6
 7use DesignPatterns\Structural\DependencyInjection\DatabaseConfiguration;
 8use DesignPatterns\Structural\DependencyInjection\DatabaseConnection;
 9use PHPUnit\Framework\TestCase;
10
11class DependencyInjectionTest extends TestCase
12{
13    public function testDependencyInjection()
14    {
15        $config = new DatabaseConfiguration('localhost', 3306, 'user', '1234');
16        $connection = new DatabaseConnection($config);
17
18        $this->assertSame('user:1234@localhost:3306', $connection->getDsn());
19    }
20}