当前位置:首页 > 日记 > 正文

PHP依赖注入 | DI和控制反转 | IoC详解

PHP依赖注入 | DI和控制反转 | IoC详解

首先依赖注入和控制反转说的是同一个东西,是一种设计模式,这种设计模式用来减少程序间的耦合,鄙人学习了一下,看TP官网还没有相关的文章,就写下这篇拙作介绍一下这种设计模式,希望能为TP社区贡献一些力量。

首先先别追究这个设计模式的定义,否则你一定会被说的云里雾里,笔者就是深受其害,百度了N多文章,都是从理论角度来描述,充斥着大量的生涩词汇,要么就是java代码描述的,也生涩。

不管怎么样,总算弄清楚一些了,下面就以php的角度来描述一下依赖注入这个概念。

先假设我们这里有一个类,类里面需要用到数据库连接,按照最最原始的办法,我们可能是这样写这个类的:

class example {    private $_db;  function __construct(){    include "./Lib/Db.php";    $this->_db = new Db("localhost","root","123456","test");  }  function getList(){    $this->_db->query("......");//这里具体sql语句就省略不写了  } }

过程:

在构造函数里先将数据库类文件include进来;
然后又通过new Db并传入数据库连接信息实例化db类;
之后getList方法就可以通过$this->_db来调用数据库类,实现数据库操作。

看上去我们实现了想要的功能,但是这是一个噩梦的开始,以后example1,example2,example3....越来越多的类需要用到db组件,如果都这么写的话,万一有一天数据库密码改了或者db类发生变化了,岂不是要回头修改所有类文件?
ok,为了解决这个问题,工厂模式出现了,我们创建了一个Factory方法,并通过Factory::getDb()方法来获得db组件的实例:

class Factory {  public static function getDb(){    include "./Lib/Db.php";    return new Db("localhost","root","123456","test");  } }

sample类变成:

class example {    private $_db;  function __construct(){    $this->_db = Factory::getDb();  }  function getList(){    $this->_db->query("......");//这里具体sql语句就省略不写了  } }

这样就完美了吗?再次想想一下以后example1,example2,example3....所有的类,你都需要在构造函数里通过Factory::getDb();获的一个Db实例,实际上你由原来的直接与Db类的耦合变为了和Factory工厂类的耦合,工厂类只是帮你把数据库连接信息给包装起来了,虽然当数据库信息发生变化时只要修改Factory::getDb()方法就可以了,但是突然有一天工厂方法需要改名,或者getDb方法需要改名,你又怎么办?当然这种需求其实还是很操蛋的,但有时候确实存在这种情况,一种解决方式是:

我们不从example类内部实例化Db组件,我们依靠从外部的注入,什么意思呢?看下面的例子:

class example {  private $_db;  function getList(){    $this->_db->query("......");//这里具体sql语句就省略不写了  }  //从外部注入db连接  function setDb($connection){    $this->_db = $connection;  } } //调用$example = new example();$example->setDb(Factory::getDb());//注入db连接$example->getList();

这样一来,example类完全与外部类解除耦合了,你可以看到Db类里面已经没有工厂方法或Db类的身影了。我们通过从外部调用example类的setDb方法,将连接实例直接注入进去。这样example完全不用关心db连接怎么生成的了。
这就叫依赖注入,实现不是在代码内部创建依赖关系,而是让其作为一个参数传递,这使得我们的程序更容易维护,降低程序代码的耦合度,实现一种松耦合。

这还没完,我们再假设example类里面除了db还要用到其他外部类,我们通过:

$example->setDb(Factory::getDb());//注入db连接$example->setFile(Factory::getFile());//注入文件处理类$example->setImage(Factory::getImage());//注入Image处理类 ...

我们没完没了的写这么多set?累不累?
ok,为了不用每次写这么多行代码,我们又去弄了一个工厂方法:

class Factory {  public static function getExample(){    $example = new example();    $example->setDb(Factory::getDb());//注入db连接    $example->setFile(Factory::getFile());//注入文件处理类    $example->setImage(Factory::getImage());//注入Image处理类    return $expample;  } }

实例化example时变为:

$example=Factory::getExample();$example->getList();

似乎完美了,但是怎么感觉又回到了上面第一次用工厂方法时的场景?这确实不是一个好的解决方案,所以又提出了一个概念:容器,又叫做IoC容器、DI容器。

我们本来是通过setXXX方法注入各种类,代码很长,方法很多,虽然可以通过一个工厂方法包装,但是还不是那么爽,好吧,我们不用setXXX方法了,这样也就不用工厂方法二次包装了,那么我们还怎么实现依赖注入呢?
这里我们引入一个约定:在example类的构造函数里传入一个名为Di $di的参数,如下:

class example {  private $_di;  function __construct(Di &$di){    $this->_di = $di;  }  //通过di容器获取db实例  function getList(){    $this->_di->get('db')->query("......");//这里具体sql语句就省略不写了  } }$di = new Di();$di->set("db",function(){  return new Db("localhost","root","root","test");  });$example = new example($di);$example->getList();

Di就是IoC容器,所谓容器就是存放我们可能会用到的各种类的实例,我们通过$di->set()设置一个名为db的实例,因为是通过回调函数的方式传入的,所以set的时候并不会立即实例化db类,而是当$di->get('db')的时候才会实例化,同样,在设计di类的时候还可以融入单例模式。

这样我们只要在全局范围内申明一个Di类,将所有需要注入的类放到容器里,然后将容器作为构造函数的参数传入到example,即可在example类里面从容器中获取实例。当然也不一定是构造函数,你也可以用一个 setDi(Di $di)的方法来传入Di容器,总之约定是你制定的,你自己清楚就行。

这样一来依赖注入以及关键的容器概念已经介绍完毕,剩下的就是在实际中使用并理解它吧!

相关文章

电脑虚拟内存怎么设置

电脑虚拟内存怎么设置

设置,虚拟内存,电脑软件,内存在电脑中发挥重要作用,电脑中所有运行的程序都需要内存来执行,如果执行的程序太多,就会导致内存消耗殆尽。为了解决这个问题,Windows采用虚拟内存,用一部分硬盘空间来充当内存使用,这部分空间即称为虚拟内存,虚拟内存…

MySQL分页性能探索

MySQL分页性能探索

性能,分页,电脑软件,MySQL,几种常见的分页方法: 1。这样的自动扶梯 自动扶梯的方式通常在前一页/下一页提供两种导航方式。有些产品甚至没有提供前一页的功能。它们只提供了一种更为丰富的方式,并且有更多的方法自动加载下拉列表,这可以推广…

JS使用tofixed与round处理数据四舍

JS使用tofixed与round处理数据四舍

四舍五入,数据,区别,电脑软件,JS,1 、tofixed方法  toFixed() 方法可把 Number 四舍五入为指定小数位数的数字。例如将数据Num保留2位小数,则表示为:toFixed(Num);但是其四舍五入的规则与数学中的规则不同,使用的是银行家舍入规则,银行家舍入:…

基于Bootstrap模态对话框只加载一

基于Bootstrap模态对话框只加载一

模态,对话框,数据,加载,解决方法,摘要: 前端框架 Bootstrap 的模态对话框,可以使用 remote 选项指定一个 URL,这样对话框在第一次弹出的时候就会自动从这个地址加载数据到 .modal-body 中,但是它只会加载一次,不过通过在事件中调用 removeData(…

让你的IIS无懈可击

让你的IIS无懈可击

无懈可击,电脑软件,IIS,如果你的电脑新安装了NT4/Win2000以后,并不是说就可以直接用来作Internet服务器了。尽管微软的补丁打了一大堆,但还是有些漏洞。现在我们就简单的谈一下如何使用IIS建立一个高安全性能的服务器。一、 以Windows NT的安…

Yii框架连接mongodb数据库的代码

Yii框架连接mongodb数据库的代码

数据库,连接,框架,代码,电脑软件,yii2框架是yii的升级版本,本文我们分别讲解在yii框架中如何连接数据库mongodb。在文件夹common/config/main_local.php中加入如下代码:<?phpreturn ['components' => ['mongodb' => ['class' => 'yii…

浅谈oracle的SCN机制

浅谈oracle的SCN机制

机制,浅谈,电脑软件,SCN,oracle,作为oracle中的一种重要机制,SCN(系统更改号)在数据恢复、数据保护、数据流复制、RAC节点间的同步等方面起着重要的作用,了解SCN的运行机制有助于您更好地理解上述功能。 在理解SCN之前,让我们看看Oracle事务中的…

Node.js v8.0.0正式发布!看看带来了

Node.js v8.0.0正式发布!看看带来了

新特性,带来了,正式发布,电脑软件,js,前言Node.js于5月30号在其官方博客上发布了Node.js v8.0.0。这一版本将成为当前的长期维护版本,从2017年10月开始到2019年12月31号。而Node.js v6.0.0将会在2018年4月进入维护模式,并于2019年4月结束。最…

简要描述redis和MySQL之间的差异

简要描述redis和MySQL之间的差异

描述,简要,差异,电脑软件,redis,我们知道MySQL是持久存储。它存储在磁盘中。如果检索,它将涉及某些IO。为了解决这一瓶颈,缓存的出现,例如,现在最常用的缓存(MC)。首先,用户访问的MC,如果不打,去mysql,然后像内存和硬盘,拷贝数据到MC的一部分。 两redi…

H5基于iScroll实现下拉刷新和上拉

H5基于iScroll实现下拉刷新和上拉

上拉加载更多,下拉刷新,电脑软件,iScroll,前言 前一段有个手机端的项目需要用到下拉刷新和上拉加载更多的效果,脑海里第一反映就是微博那种效果,刚开始的理解有些偏差,以为下拉也是追加数据,上拉也是追加数据,后请教同事后发现其实下拉只是…

使用store来优化React组件的方法

使用store来优化React组件的方法

组件,方法,优化,电脑软件,store,?在使用 React 编写组件的时候,我们常常会碰到两个不同的组件之间需要共享状态情况,而通常的做法就是提升状态到父组件。但是这样做会有一个问题,就是尽管只有两个组件需要这个状态,但是因为把状态提到了父组件,…

总结PHP中数值计算的注意事项

总结PHP中数值计算的注意事项

数值计算,注意事项,电脑软件,PHP,一:四舍五入1.round — 对浮点数进行四舍五入float round ( float $val [, int $precision ] )2:floor — 舍去法取整(向下取整)float floor ( float $value )3.ceil — 进一法取整(向上取整)float ceil ( fl…