2.6. Dependency Injection

2.6.1. Zweck

Eine lose gekoppelte Architektur zu implementieren, um besser testbaren, wartbaren und erweiterbaren Code zu erreichen.

2.6.2. Verwendung

DatabaseConfiguration gets injected and DatabaseConnection will get all that it needs from $config. Without DI, the configuration would be created directly in DatabaseConnection, which is not very good for testing and extending it.

2.6.3. Beispiele

  • Das Doctrine2 ORM benutzt Dependency Injection für z.B. die Konfiguration, die in ein Connection injiziert wird. Für Testzwecke lässt sich diese leicht durch ein gemocktes Objekt austauschen.

  • Viele Frameworks bieten bereits Dependency Injection Container an, die selbstständig vorkonfigurierte Objekte bei Bedarf erzeugen und diese in z.B. Controller injizieren können.

2.6.4. UML Diagramm

Alt DependencyInjection UML Diagram

2.6.5. Code

Du findest den Code auch auf 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. Теst

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}