视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
通俗易懂理解PHP依赖注入容器
2020-11-03 18:20:49 责编:小采
文档


依赖注入容器理解

耦合

一个好的代码结构设计一定是松耦合的,这也是很多通用设计模式的宗旨,就是把分散在各处的同一个功能的代码汇聚到一起,形成一个模块,然后在不同模块之间通过一些细小的、明确的渠道进行沟通。

在实践中,不同功能和模块之间的互相依赖是不可避免的,而如何处理好这些依赖之间的关系则是代码结构能否变得美好的关键。

<?php
class User
{
 public function register($user)
 {
 // 注册操作
 ...
 
 // 发送确认邮件
 $notify = new Notify();
 $notify->sendEmail('register', $user);
 }
}
 
class Notify
{
 public function sendEmail($type, $data)
 {
 switch $type {
 case 'register':
 // 发送注册确认邮件
 $email = new Email($type);
 $email->send($data);
 ...
 }
 }
}
 
class Email
{
 public function send($data)
 {
 // 发送邮件
 }
}

上述代码中,三个类之间逐层依赖,三个类实例化的顺序是 User -> Notify -> Email

也就是说我先实例化User类,可能执行了一些代码之后再去实例化我需要的其他类,比如Notify,以此类推。

这种依赖会让我们不得不为了得到需要的依赖而去做的一些准备工作,有时候可能一个new操作还不够。而这部分工作就是所说的耦合,他会让一个功能的类不得不去关心一些和自己的主体功能没什么关系的操作。

解除一个类对其他类的依赖

要解决这个问题也很简单,我可以先实例化好Email类,然后再实例化Notify,然后把Email对象作为参数传给Notify,最后实例化User类,然后把Notify传进去。这就是所谓的依赖注入,可以看到这个过程中类实例化的顺序完全反过来了,先实例化被依赖的对象,而不是先实例化最终需要的对象,这是控制反转。

代码如下:

<?php
$email = new Email();
$notify = new Notify($email);
$user = new User($notify);

可以通过构造函数来注入需要的依赖,也可以用一些其他的方法。

用容器托管依赖

那又有新的问题,例子中只有三个类还好,那如果这个User类依赖Notify来发邮件,依赖Model来存数据库,依赖redis来缓存,这样固然把依赖关系转移到了类的外部,但还是会导致我只想实例化一下User的时候,却要手动做很多的准备工作,会让代码混乱。所以这个时候需要一个容器。而这个容器的作用就是替我来管理这些依赖。

<?php
// 容器
class Container implements ArrayAccess
{
 protected $values = [];
 
 public function offsetGet($offset) 
 {
 return $this->values[$offset]($this);
 }
 
 public function offsetSet($offset, $value) 
 {
 $this->values[$offset] = $value;
 }
}

在程序启动的时候,我们可以在一个地方统一的注册好一系列的基础服务。

<?php
$container = new Container();
 
$container['notify'] = function($c) {
 return new Notify();
};
 
$container['email'] = function($c) {
 return new Email();
};

就会变成这样

<?php
class User
{
 public function register($user)
 {
 // 注册操作
 ...
 
 // 发送确认邮件
 $container('notify')->sendEmail('register', $user);
 }
}
 
class Notify
{
 public function sendEmail($type, $data)
 {
 switch $type {
 case 'register':
 // 发送注册确认邮件
 $email = $container['email'];
 $email->send($data);
 ...
 }
 }
}
 
class Email
{
 public function send($data)
 {
 // 发送邮件
 }
}

就是当User需要Notify的时候,会去向容器要这个类的对象,那至于Notify再依赖什么其他的东西,我就不用管了,因为Notify也会去向容器要它自己需要的依赖。所有这些依赖关系的处理就完全托管给容器了,我们既不需要去关心依赖之间的层次关系,也避免了依赖之间的耦合。

需要注意的是,依赖注入容器一般只接受一个匿名函数,而不是一个实例化好的对象,匿名函数会告诉容器怎样去获得一个对象,这样可以使得一个服务在被需要的时候才会去实例化

下载本文
显示全文
专题