控制反转(IOC)和依赖注入(DI)以php举例

首先依赖注入和控制反转说的是同一个东西,是一种设计模式,这种设计模式用来减少程序间的耦合。


要想理解 PHP 依赖注入 和 控制反转 两个概念,就必须搞清楚如下的两个问题:

  • DI —— Dependency Injection  依赖注入

  • IOC —— Inversion of Control   控制反转    

注:也可以理解IOC是一个容器

什么是依赖注入?

大白话解释如下:

所谓“依赖”,就是“我若依赖你,少了你就没有我”。------可怕的依赖


没有你我就活不下去,那么,你就是我的依赖。 说白了就是:

不是我自身的,却是我需要的,都是我所依赖的。一切需要外部提供的,都是需要进行依赖注入的。


代码层解释如下:

只要不是由内部生产(比如初始化、构造函数 __construct 中通过工厂方法、自行手动 new 的),而是由外部以参数或其他形式注入的,都属于 依赖注入(DI) 。是不是豁然开朗?


看以下代码:

//轮胎类
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



声明:禁止任何非法用途使用,凡因违规使用而引起的任何法律纠纷,本站概不负责。

扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

精彩评论

全部回复12人评论7,777人参与