0%

设计模式-控制反转及其依赖注入(1)

摘要


IoC:Inversion of Control 控制反转

DI:Dependency Injection 依赖注入

IoC与DI


先记住这句话:IoC是设计模式,而DI是IoC控制反转设计模式的典型实现

IoC控制反转是一种设计模式,用来解决对象间的过度依赖问题。

解决思路是:设法不在依赖对象中去获取(new)被依赖对象,最典型的的实现方式就是DI依赖注入了。

将对象所依赖的其他对象,在类外部生成好之后,传递到类内部的,而不是在类的内部实例化。这种解决依赖的方法就是DI依赖注入。

例如:对象Hero依赖对象Sword,我们可以选择如下定义方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 英雄依赖宝剑的定义实现
class Sword
{
private $title;
public function __construct($title)
{
$this->title = $title;
}
}
class Hero
{
private $weapon;
public function __construct()
{
// Hero依赖Sword
$this->weapon = new Sword('倚天剑');
}
}

上面的代码中,Hero类对象就依赖Sword对象,但是在此例子中,Hero类对象对于Sword类对象的依赖就比较严重,一旦这个Hero使用的不再是Sword,而是Gun了,Hero的内部方法就要重写,在复杂的程序中,是不可取得,需要降低Hero对武器(无论是Sword或者Gun亦或者是其他武器)的直接依赖

解决思路就是设法不在依赖对象中去获取需要依赖的对象,这种思路就是IoC控制反转。

把原来本应在类(对象)内部完成的依赖,设法在类(对象)外部完成,这个由内到外的转化过程就是反转

所以:IoC反转最典型的实现方式就是依赖注入DI,如下代码所示

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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
/**
* 武器接口
* Interface Weapon
*/
interface Weapon
{
public function __construct($title);
/**
* 进攻
*/
public function attack();
}

/**
* 宝剑类
*/
class Sword implements Weapon
{
protected $title;
public function __construct($title)
{
$this->title = $title;
}
public function attack()
{
return "我的{$this->title}打起来唰唰唰~";
}


/**
* 枪类
*/
class Gun implements Weapon
{
protected $title;
public function __construct($title)
{
$this->title = $title;
}
public function attack()
{
return "我的{$this->title}打起来砰砰砰~";
}
}

class Hero
{
private $weapon;

/**
*
* 构造方法的参数是一个对象,通过类型约束限制必须为实现Weapon武器接口的对象
* 在构造方法中,直接将参数传递进来的武器对象赋值到当前对象的属性上
* 这样,英雄Hero依赖的对象不是在Hero类的内部实例化,而是在外部实例化好,传递到Hero内部的
*
* 这就是《依赖注入》,通俗来讲,就是所依赖的对象在外部生成好之后,传递到类内内部的,而不是在
* 类内部实例化,这种解决依赖的方法就是DI依赖注入
*
*
*
* Hero constructor。
*
* @param Weapon $weapon
*/
public function __construct(Weapon $weapon)
{
$this->weapon = $weapon;
}
public function myWeapon()
{
echo $this->weapon->attack();
}

调用测试:

1
2
3
4
5
6
7
8
//用宝剑的英雄
$class = new Hero(new Gun('倚天'));
$class->myWeapon();
//我的倚天打起来唰唰唰~
//用枪的英雄
$class = new Hero(new Sword('沙漠之鹰'));
$class->myWeapon();
//我的沙漠之鹰打起来砰砰砰~

这样,无论Hero需要宝剑还是枪,都可以通过外部注入的方式,将武器传递给Hero对象

通过构造方法传递参数,是依赖注入最常用的形式,除此之外,还有属性赋值的方法,也可以完成依赖注入,例如:

1
2
3
4
5
6
7
// 
class Hero
{
public $weapon;
}
$hero = new Hero;
$hero->weapon = new Sword('倚天');

以上就是平时所说的依赖注入,有没有理解呢?

结语


解决了什么是依赖注入的问题, 本篇的目的就达到了,(示例代码在这里,本文参考引用这里)
但还远远不够 , 注意上面的使用Hero的代码, 我们是手动将实例化好的武器对象作为参数传递给Hero的构造方法的。 此时的问题就是, 当出现大量的, 随机的需要注入的依赖如何处理? 一个个的实例化传递, 是否够自动化?

要解决这个问题, 就出现了IoC容器。 IoC容器也称为服务容器。 主要就是解决依赖和注入的问题。 实现机制是通过预先将创建对象的代码绑定或注册到IoC容器中, 然后利用该IoC容器创建对象, 在创建对象的过程中, 通过分析对象所需要的依赖(一般利用反射机制), 将注册好的创建对象的代码注入到对象的构造方法中去, 从而完成自动解决这个依赖注入的问题。 非常智能。

下篇我会接着记录