PHP 依赖注入模式

目的

实现了松耦合的软件架构,可得到更好的测试,管理和扩展的代码

用例

通过配置需要注入的依赖,Connection 能从 $config 中获取到所有它需要的依赖。如果没有依赖注入,Connection 会直接创建它需要的依赖,这样不利于测试和扩展 Connection。

例子

  • Doctrine2 ORM 使用了依赖注入,它通过配置注入了 Connection 对象。为了达到方便测试的目的,可以很容易的通过配置创建一个mock的 Connection 对象。
  • 许多框架已经有用于DI的容器,这些容器通过配置数组创建对象,并在需要的地方(即在控制器中)注入它们。

UML 图

Alt DependencyInjection UML Diagram

代码

DatabaseConfiguration.php

<?php

declare(strict_types=1);

namespace DesignPatterns\Structural\DependencyInjection;

class DatabaseConfiguration
{
    public function __construct(
        private string $host,
        private int $port,
        private string $username,
        private string $password
    ) {
    }

    public function getHost(): string
    {
        return $this->host;
    }

    public function getPort(): int
    {
        return $this->port;
    }

    public function getUsername(): string
    {
        return $this->username;
    }

    public function getPassword(): string
    {
        return $this->password;
    }
}

DatabaseConnection.php

<?php

declare(strict_types=1);

namespace DesignPatterns\Structural\DependencyInjection;

class DatabaseConnection
{
    public function __construct(private DatabaseConfiguration $configuration)
    {
    }

    public function getDsn(): string
    {
        // this is just for the sake of demonstration, not a real DSN
        // notice that only the injected config is used here, so there is
        // a real separation of concerns here

        return sprintf(
            '%s:%s@%s:%d',
            $this->configuration->getUsername(),
            $this->configuration->getPassword(),
            $this->configuration->getHost(),
            $this->configuration->getPort()
        );
    }
}

测试

Tests/DependencyInjectionTest.php

<?php

declare(strict_types=1);

namespace DesignPatterns\Structural\DependencyInjection\Tests;

use DesignPatterns\Structural\DependencyInjection\DatabaseConfiguration;
use DesignPatterns\Structural\DependencyInjection\DatabaseConnection;
use PHPUnit\Framework\TestCase;

class DependencyInjectionTest extends TestCase
{
    public function testDependencyInjection()
    {
        $config = new DatabaseConfiguration('localhost', 3306, 'domnikl', '1234');
        $connection = new DatabaseConnection($config);

        $this->assertSame('domnikl:1234@localhost:3306', $connection->getDsn());
    }
}