Skip to content

Menu
Menu

依赖注入容器

Posted on 2023年2月26日2023年7月13日 by zhezimi

依赖注入是一种允许我们从硬编码的依赖中解耦出来,从而在运行时或者编译时能够修改的软件设计模式。

这句解释让依赖注入的概念听起来比它实际要复杂很多。依赖注入通过构造注入,函数调用或者属性的设置来提供组件的依赖关系。就是这么简单。

基本概念

我们可以用一个简单的例子来说明依赖注入的概念。

下面的代码中有一个 Shop 的类,它需要一个Mysql类来与数据库交互。我们在构造函数里实例化了Mysql,从而产生了耦合。这会使测试变得很困难,而且 Shop类和Mysql类耦合的很紧密。

<?php

namespace app\services;

class Mysql
{
    public function connect(): string
    {
        return "this is mysql link";
    }
}

<?php

namespace app\services;

class Shop
{
protected $db;

public function __construct()
{
$this->db = new Mysql();
}

public function connect(): string
{
return $this->db->connect();
}
}
$shop = new Shop();
echo $shop->connect();

在这种情况下,我们使用new 的方式在内部进行,Shop类严重依赖于Mysql类。每当Mysql类变化时,Product类也得进行变化。

依赖注入

我们可以用依赖注入重构代码,从而实现解耦

<?php

namespace app\services;

class Mysql
{
    public function connect(): string
    {
        return "this is mysql link";
    }
}

<?php

namespace app\services;

class Shop
{
protected $db;

public function __construct(Mysql $db)
{
$this->db = $db;
}

public function connect(): string
{
return $this->db->connect();
}
}
$shop = new Shop(new Mysql());
echo $shop->connect();

我们对程序进行了解耦。目前的话,Mysql类无论如何变动,Shop类是不需要变动的,不再依赖于Mysql类。

新问题:假如后期我们把数据库换成了SqlServer,那么这时候Shop类还是得改动。

依赖反转准则

依赖反转准则是面向对象设计准则 S.O.L.I.D 中的 “D” ,倡导 “依赖于抽象而不是具体”。简单来说就是依赖应该是接口或者抽象类,而不是具体的实现。我们能很容易重构前面的代码,使之遵循这个准则

<?php

namespace app\services;

interface Database
{
public function connect();
}
<?php

namespace app\services;

class Mysql implements Database
{
public function connect(): string
{
return "this is mysql link";
}
}

<?php

namespace app\services;

class Shop
{
protected $db;

public function __construct(Database $db)
{
$this->db = $db;
}

public function connect(): string
{
return $this->db->connect();
}
}
$shop = new Shop(new Mysql());
echo $shop->connect();

现在 Shop类依赖于接口,后期我们可以随意将数据库换成SqlServer,Oracle,而不需要修改Shop类。

新问题:我们目前只是一个简单的示例,Shop类当只要实例化一个Mysql类,但是现实当中,你的依赖会逐渐增多。例如A依赖B,B依赖C,C依赖D,层层依赖,那么相当于我们要实例化A类的话,需要编写以下代码:

$a=new A(new B(new C(new D)))

也就是不论我们的依赖有多少层,如何自动地去引入相应地依赖呢?那么需要用到容器了。

依赖注入容器

你应该明白的第一件事是依赖注入容器和依赖注入不是相同的概念。依赖注入容器是帮助我们更方便地实现依赖注入的工具。

下面我们PHP-DI来优化一下代码

<?php

namespace app\services;

interface Database
{
    public function connect();
}
<?php

namespace app\services;

class Mysql implements Database
{
public function connect(): string
{
return "this is mysql link";
}
}
<?php

namespace app\services;

class Shop
{
protected $db;

public function __construct(Database $db)
{
$this->db = $db;
}

public function connect(): string
{
return $this->db->connect();
}
}
$container = new \DI\Container();
$container->set(Database::class, \DI\create(Mysql::class));
$shop = $container->get(Shop::class);
echo $shop->connect();

我们可以看到PHP-DI会自动解决依赖关系, 将依赖实例化并注入新创建的对象。依赖关系的解决是递归的, 如果一个依赖关系中还有其他依赖关系, 则这些依赖关系都会被自动解决。

容器的具体实现是用了PHP的反射。大家可以多看看反射的参考文档。

参考链接:

https://php-di.org/

https://laravel-china.github.io/php-the-right-way/#further_reading

《深入PHP面向对象,模式与实践》(第5版)

相关文章

  • PHP-FPM异常问题

  • PHP7新特性

  • 生成器

  • 错误与异常

  • 浮点数精度问题

发表评论 取消回复

您的电子邮箱地址不会被公开。 必填项已用*标注

近期文章

  • 排查网络故障常用命令
  • PHP-FPM异常问题
  • RabbitMQ 1:介绍
  • 观察者模式
  • 装饰者模式

近期评论

没有评论可显示。

分类

  • cdn
  • css
  • docker
  • git
  • http
  • javascript
  • linux
  • mysql
  • nginx
  • php
  • RabbitMQ
  • 代码规范
  • 性能
  • 正则表达式
  • 网络协议
  • 设计模式
© 2025 | Powered by Minimalist Blog WordPress Theme