首先依赖注入和控制反转说的是同一个东西,是一种设计模式,这种设计模式用来减少程序间的耦合。
要想理解 PHP 依赖注入 和 控制反转 两个概念,就必须搞清楚如下的两个问题:
DI —— Dependency Injection 依赖注入
IOC —— Inversion of Control 控制反转
注:也可以理解IOC是一个容器
什么是依赖注入?
大白话解释如下:
所谓“依赖”,就是“我若依赖你,少了你就没有我”。------可怕的依赖
没有你我就活不下去,那么,你就是我的依赖。 说白了就是:
不是我自身的,却是我需要的,都是我所依赖的。一切需要外部提供的,都是需要进行依赖注入的。
代码层解释如下:
看以下代码:
//轮胎类 class LunTai { public function roll() { echo "轮胎在滚动<br/>"; } } //宝马汽车类 class BMW { //宝马汽车必须有轮胎才能跑,所以这里需要轮胎类 public function run() { $luntai = new LunTai(); $luntai->roll(); echo "开着宝马吃烤串<br/>"; } } $bmw = new BMW(); $bmw->run(); //输出:轮胎在滚动 && 开着宝马吃烤串
代码如果这样写的话 是可以的,但是这样写不好,因为这两个类紧密联系,BMW类依赖于LunTai类,两个类之间有着高强度的耦合度,如果今后开发过程中,要对LunTai类修改,一旦涉及函数改名,函数参数数量变动,甚至整个类结构的调整,我们也要对BMW类做出相应的调整,BMW类的独立性丧失了,这在开发过程中是很不方便的,也就是我们说的“牵一发动全身”,如果两个类是两个人分别写的,矛盾往往就在这个时候产生了。。
万一真的要改动LunTai类,有没有办法,可以不去改动或者尽量少改动A类的代码呢?这里要用到控制反转。
高层模块不应该依赖于底层模块,两个都应该依赖抽象。
控制反转(IOC)是一种思想,依赖注入(DI)是实施这种思想的方法。
实现方式有很多种比如:构造器(也就是构造方法)注入、工厂模式注入、工厂模式升华版->IOC容器(现在各大框架源代码都有在使用这种方式 比如thinkphp5 laravel yii2......)等。
1、构造器注入(这种方法也不推荐用,但比不用要好,这里只列举构造器方式注入的demo),代码如下:
//轮胎类 class LunTai { public function roll() { echo "轮胎在滚动<br/>"; } } //宝马汽车类 class BMW { protected $luntai = null; //构造函数注入方式,将LunTai类的对象作为参数。传递给BMW类(这个操作就叫做依赖注入) public function __construct($luntai) { $this->luntai = $luntai; } public function run() { $this->luntai->roll(); echo "开着宝马吃烤串<br/>"; } } $luntai = new LunTai(); $bmw = new BMW($luntai); $bmw->run(); //输出:轮胎在滚动 && 开着宝马吃烤串
这种方式相比较上面那种方式会好很多,通过这种依赖注入的方式就可以减少两个类直接的耦合度,因为是使用传递的方式传递给BMW类,而不是和上面第一种方式一样,在BMW类里面直接创建LunTai类的对象(因为这种方式会直接让LunTai类和BMW类有着直接的紧密联系)。
什么是IOC容器?
容器按照字面上的理解就是装东西的东西。常见的变量、对象属性等都可以算是容器。一个容器能够装什么,全部取决于你对该容器的定义。当然,有这样一种容器,它存放的不是文本、数值,而是对象、对象的描述(类、接口)或者是提供对象的回调,通过这种容器,我们得以实现许多高级的功能,其中最常提到的,就是 “解耦” 、“依赖注入(DI)”。比如冰箱, 当我们需要冰箱里面的东西的时候直接从里面拿就行了。代码中的容器也可以这样理解, 当程序开始运行的时候,我们把我们需要的一些服务放到或者注册到 (bind) 到容器里面,当我需要的时候直接取出来 (make) 就行了。上面提到的 bind 和 make 就是 注册 和 取出的 两个动作。
好了,说了这么多,下面将上面的两个类的代码改为IOC容器方式的代码了,不过需要理解php中的闭包函数(匿名函数)代码如下:
//轮胎类 class LunTai { public function roll() { echo "轮胎在滚动<br/>"; } } //宝马汽车类 class BMW { protected $luntai = null; public function __construct($luntai) { $this->luntai = $luntai; } public function run() { $this->luntai->roll(); echo "开着宝马吃烤串<br/>"; } } //容器类 class Container { //存放所绑定的类 public static $register = array(); /** * 绑定函数 * @param string $name 类的名字 * @param object $clo 一个闭包函数 */ public static function bind($name, Closure $clo) { self::$register[$name] = $clo; } /** * 根据名字创建对象 * @param string $name 类名 * @return */ public static function make($name) { $clo = self::$register[$name]; //获取键对应的闭包函数 return $clo(); //执行该闭包函数 } } //测试 Container::bind('luntai', function(){ //return '执行了闭包里面的代码'; //看不懂的话 可以这样一步一步debug调试 return new LunTai(); }); Container::bind('bmw', function(){ return new BMW(Container::make('luntai')); }); $bmw = Container::make('bmw'); $bmw->run(); //输出:轮胎在滚动 && 开着宝马吃烤串
以上代码就是一个比较简洁的IOC容器代码(实现了IOC容器的核心思想理念以及IOC容器这种方式的核心代码)。小伙伴完全可以试着运行一下上面这种IOC容器代码,然后哪里不懂就在哪里输出调试一下(如果你会使用xdebug来调试那就事半功倍了),这样会更有助于理解。
说白了 就是创建对象不是直接去new某个类,而是交给一个第三方的类(在这里就是我们的IOC容器)去负责做这种事情,这样就达到了其中一个好处的目的:降低耦合度,实现解耦。控制反转和依赖注入指的就是以上IOC容器代码的实现方式,原理就是由一个第三方的类来负责处理创建对象等操作。
也可以粗滤的理解就是在使用类之前先把类实例化,然后把类的实例当作参数往下传递 而不是等真的用的时候再去new。
Laravel的核心就是一个IOC容器,根据文档,称其为“服务容器”,顾名思义,该容器提供了整个框架中需要的一系列服务。服务容器是整个laravel的核心,它提供了整个系统功能及服务的配置,调用。
DI依赖注入、容器
减少类和类之间的联系
容器的优点:
降低耦合度
实现队惰性加载
便于管理
可参考以下链接:
https://www.cnblogs.com/i6010/articles/10559630.html
https://www.cnblogs.com/sweng/p/6392336.html
https://segmentfault.com/a/1190000010846788
https://learnku.com/articles/4076/how-to-understand-laravels-ioc-container
声明:禁止任何非法用途使用,凡因违规使用而引起的任何法律纠纷,本站概不负责。
精彩评论