Laravel学习笔记(一)--- 依赖注入和IoC容器
依赖注入(DI)
假设实现一个汽车类Car
,它依赖一个引擎类Engine
:1
2
3
4
5
6
7
8
9class Car
{
public function go()
{
$engine = new Engine();
$engine->start();
echo "go";
}
}
这时如果汽车的Engine
变为TurboEngine
的时候,就需要修改汽车类Car
的实现。因此为了解耦,我们需要把引擎类注入
到汽车类中:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16class Car
{
protected $engine;
public function __construct($engine)
{
$this->_engine = $engine;
}
public function go()
{
$this->_engine->start();
echo "go";
}
}
$engine = new Engine();
$car = new Car($engine);
这样就实现了Car
类和Engine
类的解耦,这种方法就是依赖注入(Dependency Injection)
依赖注入一般有三种方法:
- 构造函数
- setter
- 接口
这种使用类似于依赖注入的方式来管理依赖,实现解耦的设计模式就是IoC模式(控制反转模式,Inversion of Control)
p.s. 关于IoC,这篇文章讲的比较通俗。
IoC容器
假设Car
类变得很复杂:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21class Car
{
protected $engine;
protected $tire;
public function __construct(Engine $engine,Tire $tire)
{
$this->_engine = $engine;
$this->_tire = $tire;
}
public function go()
{
$this->_tire->warm();
$this->_engine->start();
echo "go";
}
}
$tire = new Tire();
$fuel = new Fuel();
$engine = new Engine($fuel);
$car = new Car($engine, $tire);
这样带来的一个问题是,当我需要实例化Car
的时候,需要首先去实例化很多它依赖的类。让这么懒的我去做这种事情是不现实的。这样就引出了IoC容器这个东西。
IoC容器就是用来解析类的依赖,绑定接口和实现的一个东西。说起来比较抽象,还是举个栗子:
Lavarel中的APP就是一个IoC容器,它是Application类的一个实例,Application类继承了Container。下面是一个Case(来自这里):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
30class Car {
protected $tire;
protected $engine;
public function __construct(Tire $tire, Engine $engine) {
$this->tire = $tire;
$this->engine = $engine;
}
}
class Tire {}
class Engine {}
App::bind('Car', function()
{
return new Car(new Tire, new Engine);
});
Route::get('/', function()
{
dd(App::make('Car')); // dd是一个类似var_dump的方法
});
//输出:
object(Car)[145]
protected 'tire' =>
object(Tire)[143]
protected 'engine' =>
object(Engine)[150]
bind
方法是把Car
绑定到容器中,当调用make
时,就可以得到一个Car
的实例。
更NB的地方在于:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25class Car {
protected $tire;
protected $engine;
public function __construct(Tire $tire, Engine $engine) {
$this->tire = $tire;
$this->engine = $engine;
}
}
class Tire {}
class Engine {}
Route::get('/', function()
{
dd(App::make('Car'));
});
//注意没有bind了
//输出:
object(Car)[152]
protected 'tire' =>
object(Tire)[153]
protected 'engine' =>
object(Engine)[154]
这里仍然能够正常运行,因为Laravel的容器做了这些事情:
- 检查用户是否绑定了Car?(如果已经绑定,就直接使用);
- 如果没有绑定, 就查询Car的依赖关系;
- 解析所有Car需要的依赖关系(Tire 和 Engine);
- 创建Car的新实例,包含Car所依赖的所有关系;
这里不管类多复杂,依赖层次多么深,容器都会帮你搞定依赖关系。
实现
这么NB的东西是怎么实现的呢?
应该是靠PHP的反射来做的,具体等读读源码看看。