🔍

依赖注入和容器

概念

三个概念

  1. 依赖倒置原则(Dependence Inversion Principle ,缩写为 DIP),一种软件设计原则,是一个抽象的概念。
  2. 控制反转(Inversion of Control ,缩写为 IoC),DIP 的一种具体实现方式。
  3. 依赖注入(Dependency injection ,缩写为 DI),IOC 的具体实现方式。

扪心自问

什么是反转?对应的什么是正转?依赖谁?依赖什么?注入谁?注入什么?一个日常生活的例子,可以解惑。

结论

结论就是,造车难,买车容易,在写程序的时候使用依赖注入可以更好的解耦。

自己造车

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
<?php
class Car
{
    //汽车可以跑,定义一个方法 run()
    public function run(){
        return '滴滴滴,车开了';
    }
}

class Person
{
    private $car = null;//保存某人的车,暂时还没车
    public function drive(){
        $this->car = new Car();//要开车先造车,造了一辆车保存在某人的 $car 里
        return $this->car->run();//调用车的 run() 方法
    }
}

$xiaoming = new Person();
echo $xiaoming->drive();//输出 滴滴滴,车开了

造车太难了(依赖注入)

这里的依赖是动词。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<?php
class Car
{
    //汽车可以跑,定义一个方法 run()
    public function run()
    {
        return '滴滴滴,车开了';
    }
}

class Person
{
    private $car = null;//保存某人的车,暂时还没车

    //new 某人的时候,就给他注入一辆车,通过 $a 传入构造方法,并保存在 $car 里
    public function __construct($a)
    {
        $this->car = $a;
    }

    public function drive()
    {
        return $this->car->run();//调用车的 run() 方法
    }
}
$car  = new Car();//买一辆车
$xiaoming = new Person($car);//new 小明的时候,把刚才买的车注入
echo $xiaoming->drive();//输出 滴滴滴,车开了

万一 new 小明的时候,给他买了一架飞机,小明不会开飞机怎么办,通过类型限定解决这个问题。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
class Person
{
   .
   .
   .
    public function __construct(Car $a)// <----这里做类型限定,保证不会注入飞机,也不会注入火箭
    {
        $this->car = $a;
    }
	.
	.
	.

IoC 容器

在上例中,我们是手动的去 new 一辆车并注入给某人,现在用一个容器统一的去管理这些需要注入的类。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
<?php
class Car
{
    //汽车可以跑,定义一个方法 run()
    public function run()
    {
        return '滴滴滴,车开了';
    }
}

class Person
{
    private $car = null;//保存某人的车,暂时还没车

    //new 某人的时候,就给他注入一辆车,通过 $a 传入构造方法
    public function __construct(Car $a)// <----这里做了类型限定
    {
        $this->car = $a;
    }

    public function drive()
    {
        return $this->car->run();//调用车的 run() 方法
    }
}

//写一个简单的 IoC 容器类
class Container
{
    private static $objArr = [];//定义一个静态的空数组

    public static function set($flag, Callable $func){
        self::$objArr[$flag] = $func;//存入键值对,键是一个字符串,作为标识符,值是一个匿名函数
    }

    public static function get($flag){
        $tmp = self::$objArr[$flag];//取出标识符对应的匿名函数,用$tmp临时保存一下
        return $tmp();//在$tmp后名加上括号,表示执行这个函数,并返回
    }
}

//下面这条语句执行完毕后,会在 $objArr 里存入一个键值对,键是 car ,值是这个匿名函数,该匿名函数返回的是创建 Car 对象的语句
Container::set('Car', function(){
    return new Car();
});

//下面这条语句执行完毕后,会在 $objArr 里存入一个键值对,键是 person ,值是这个匿名函数,该匿名函数返回的是创建 Person 对象的语句
Container::set('Person', function(){
    return new Person(Container::get('Car'));//直接去容器中取一辆车出来,并作为参数传给 Person 类的构造函数
});

$xiaomin = Container::get('Person');//直接去容器中取一个人出来,取名叫小明
echo $xiaomin->drive();//输出 滴滴滴,车开了