php依赖注入和控制反转和服务容器的简单理解

  • 2019-05-23
  • PHP
摘要:php的依赖注入,控制反转,服务容器的简单理解。其实有些东西我们平时可能都会用到,但是没有真正了解过它,当时翻翻文档看看代码,原来就是这个意思。

百度搜索“依赖注入”,百度百科显示的却是控制反转(Inversion of Control,缩写为IoC),是面对对象设计编程的一种原则,用来降低代码的耦合度。其中最常见的方式叫做依赖注入(Dependency Injection,简称DI)。

主要技术描述就是:

Class A 中用到了 Class B 的对象 b,一般情况下,需要在 A 的代码中显式的 new 一个 B 的对象。【这种操作好像工作中经常用到,其实这就是 Class A 在 依赖 Class B】

采用依赖注入技术之后,A 的代码只需要定义一个私有的 B 对象,不需要直接 new 来获得这个对象,而是通过相关的容器控制程序来将 B 对象在外部 new 出来并注入到 A 类里的引用中。而具体获取的方法、对象被获取时的状态由配置文件(如XML)来指定。【这种方式好像一般不常用,都是框架底层来实现】

什么是依赖?

<?php
/**
 * 米其林轮胎类.
 * Class MichelinTire
 */
class MichelinTire
{
    public function type()
    {
        return 'Primacy 3 ST浩悦轮胎';
    }
}

/**
 * 汽车类.
 * Class Car
 */
class Car
{
    protected $tire;

    public function action()
    {
        // 内部实例化一个米其林轮胎类
        $this->tire = new MichelinTire();
        return '生产汽车,轮胎型号是' . $this->tire->type();
    }
}

$car = new Car();
echo $car->action();

// 生产汽车,轮胎型号是Primacy 3 ST浩悦轮胎

上面这种就是依赖(汽车类依赖轮胎类),汽车类想使用米其林轮胎类,然后在内部 new 了一个米其林轮胎类供自己使用,这种写法工作中非常常见。

但是这种写法导致代码耦合度非常高,如果我们想换一个马牌(Continental)的轮胎类,还得需要改造下汽车类,这样每换一个轮胎就得改造一下汽车类。如果要生产 100 个不同轮胎规格的汽车,就需要改 100 次汽车类。

如果我们使用依赖注入就可以很好的解决这种问。

什么是依赖注入?

<?php
/**
 * 米其林轮胎类.
 * Class MichelinTire
 */
class MichelinTire
{
    public function type()
    {
        return 'Primacy 3 ST浩悦轮胎';
    }
}

/**
 * 汽车类.
 * Class Car
 */
class Car
{
    protected $tire;

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

    public function action()
    {
        return '生产汽车,轮胎型号是' . $this->tire->type();
    }
}

$michelinTire = new MichelinTire();
$car = new Car($michelinTire);
echo $car->action();

// 生产汽车,轮胎型号是Primacy 3 ST浩悦轮胎

这次我们没有通过在汽车类中直接实例化轮胎类,而是通过在外部实例化好轮胎类对象然后通过构造方法注入到汽车类中,这种操作形式就叫做依赖注入。这样做的好处就是我们每次需要制造什么型号轮胎的汽车就不用每次都修改汽车类了。这样做就可以极大的降低了两类之间的耦合度。

依赖注入有三种方式:构造方法注入、属性注入、接口注入。

这种方式也叫做控制反转,就是把汽车类对轮胎类的控制权抽离出来然后反转给第三方(ICO)容器去做。控制反转其实是一种思想而不是一种技术,而依赖注入就是实现这种思想最经典的方法。

什么是服务容器?

<?php
/**
 * 米其林轮胎类.
 * Class MichelinTire
 */
class MichelinTire
{
    public function type ()
    {
        return 'Primacy 3 ST浩悦轮胎';
    }
}

/**
 * 汽车类.
 * Class Car
 */
class Car
{
    protected $tire;

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

    public function action()
    {
        return '生产汽车,轮胎型号是' . $this->tire->type();
    }
}

/**
 * 服务容器.
 * Class Application
 */
class Application
{
    /**
     * 已绑定的服务(闭包形式).
     * @var
     */
    protected $services = [];

    /**
     * 已绑定的服务(普通对象形式).
     * @var
     */
    protected $instances = [];

    /**
     * 将服务绑定到服务中去.
     * @param $className
     * @param $concrete
     */
    public function bind($className, $concrete)
    {
        if ($concrete instanceof Closure) { // 检测是否闭包
            $this->services[$className] = $concrete;
        } else {
            $this->instances[$className] = $concrete;
        }
    }

    /**
     * 从服务容器中解析对象.
     * @param $className
     * @param string $parameters
     * @return mixed
     */
    public function make($className, $parameters = '')
    {
        if (isset($this->instances[$className])) {
            return $this->instances[$className];
        }

        return call_user_func($this->services[$className], $this, $parameters);
    }
}

// 创建一个容器
$app = new Application;

// 绑定一个汽车类
$app->bind('Car', function($app, $moduleName) {
    return new Car($app->make($moduleName));
});

// 绑定一个轮胎类
$app->bind('MichelinTire', function($app) {
    return new MichelinTire;
});

// 生产一个米其林规格的轮胎
$car = $app->make('Car', 'MichelinTire');
echo $car->action();

// 生产汽车,轮胎型号是Primacy 3 ST浩悦轮胎

上面的这个 Application 类就是一个服务容器,实例化的 $app 就是服务容器对象,汽车类和轮胎类相当于某个服务。

$app->bind():就是把服务(一个闭包函数)绑定到容器中(数组中)。其实就是把一个闭包函数绑定到数组中,这就是别人问容器是怎么实现的?就是定义了一个数组而已!但是绑定的不是对象本身,而是对象生成代码,因为绑定时,并没有去实例化对象。

$app->make():就是从服务容器中(根据类名然后从数组中)解析服务,然后调用对象生成代码,得到相应的服务对象。

其实我对服务容器的理解大概就是

这里借用 laravel 文档中的一句话:“Laravel 服务容器是一个用于管理类的依赖和执行依赖注入的强大工具。依赖注入这个花哨名词实质上是指:类的依赖通过构造函数,或者某些情况下通过 "setter" 方法 "注入" 到类中。”

我感觉服务容器比较偏向底层框架结构部分的一种思想和技术,其实工作中一般写一个业务层几乎用不到,但是它是一种设计模式,一种思想,一种面向对象的编程思想,主要就是对代码的解耦,上面这个只是一个简单的容器,真正的服务容器它用到很多东西,比如:闭包、依赖注入、反射、工厂模式等等。

依赖是什么?就是一个类需要用到另外一个类,这两个类就是依赖。

依赖注入是什么?就是将一个类通过注入的方式(比如:构造方法、属性、接口)注入到另外一个类中。

控制反转是什么?这就是一种思想了,就是将我依赖这个类的控制权交给(反转)给第三方来处理,我不在控制什么,我只管使用就好。比如刚才的汽车类和轮胎类,汽车类不在控制轮胎类,而是我们给它注入一个什么轮胎就生产一个什么规格轮胎的汽车。

服务容器是什么?我感觉就是一个管理类和生产类的一个工厂。其实刚才 laravel 文档中说的很好,服务容器是用来管理类的依赖和执行依赖注入的强大工具。这个“管理”二字用的非常好。

参考文章:

https://www.insp.top/learn-laravel-container

https://zhuanlan.zhihu.com/p/33492169

结束语:感谢您对本网站文章的浏览,欢迎您的分享和转载,但转载请说明文章出处。
Top