Laravel
服务容器是用于管理类的依赖和执行依赖注入的工具。依赖注入这个花俏名词实质上是指:类的依赖项通过构造函数,或者某些情况下通过「setter
」方法「注入」到类中。
DI
DI
就是常说的依赖注入,那么究竟什么是依赖注入呢?
打个比方,电脑(非笔记本哈)需要键盘和鼠标我们才能进行操作,这个‘需要’换句话说就是‘依赖’键盘和鼠标。
那么,相应的,一个类需要另一个类才能进行作业,那么这也就是依赖。
看一段代码:
1 2 3 4 5 6 7 8
| class Computer { protected $keyboard; public function __construct() { $this->$keyboard = new Keyboard(); } }
|
好,既然我们已经知道了什么是依赖,那么什么是注入呢?
我们改造一下上面的代码:
1 2 3 4 5 6 7 8 9 10 11
| class Computer { protected $keyboard; public function __construct(Keyboard $keyboard) { $this->$keyboard = $keyboard; } }
$computer = new Computer(new Keyboard());
|
关于依赖注入,我的理解是:
所需要的类通过参数的形式传入的就是依赖注入。
理解了依赖注入,我们可以接着理解IOC
。
IOC
IOC
是什么呢?
中文叫控制反转。啥意思呢? 这个看明白了DI
后就能很容易的理解了。
通过DI我们可以看到,一个类所需要的依赖类是由我们主动实例化后传入类中的。
控制反转和这个有什么关系呢?
控制反转意思是说将依赖类的控制权交出去,由主动变为被动。
看一段laravel
代码:
1 2 3 4 5 6 7 8 9 10
| namespace App\Http\Controllers; use Illuminate\Http\Request;
class SessionController extends Controller{ public function login(Request $request) { } }
|
看到这你可能有疑问了,这是怎么实现的呢?
这就是靠服务容器了,请往下接着看。
服务容器
看了很多文章,我一致认为服务容器就是一种设计模式。
它的目的就是解耦依赖。
它有点类似于我前面说的《享元模式》。区别在于服务容器解决了所有依赖的实现。
这里我们再从头至尾的看一遍,怎么一步步演化出服务容器。
依然是电脑的例子,我们知道电脑依赖键盘鼠标,可是键盘鼠标也有很多种呀。
先看一个最原始的代码例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| class Computer { protected $keyboard; public function __construct($type == null) { switch($type) { case 'common': $this->keyboard = new CommonKeyboard(); break; case 'awesome': $this->keyboard = new AweSomeKeyboard(); break; default: $this->keyboard = new Keyboard(); break; } } }
|
或许你一眼就看出了问题在哪。
如果我们又要增加一钟键盘,那我们又得对这个类进行修改。这样下去,这个类会变得庞大且耦合程度过高。
那么我们可以怎么修改呢?
这样我们可以避免直接的修改Computer
类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| class Factory { public static function getInstance($type){ switch($type) { case 'common': $this->keyboard = new CommonKeyboard(); break; case 'awesome': $this->keyboard = new AweSomeKeyboard(); break; default: $this->keyboard = new Keyboard(); break; } } }
class Computer { protected $keyboard; public function __construct($type == null) { $this->keyboard = Factory::getInstance($type); } }
|
这样使用简单工厂模式后,我们后续的修改可以不用对Computer
类进行操作而只要修改工厂类就行了。这就相当于对Computer
类进行了解耦。
Computer
类虽不在依赖那些键盘类了,但是却变为依赖工厂类了。
后续添加新类型的键盘就必须对工厂类进行修改。
所以这个工厂类还不能很好的满足要求,我们知道电脑对键盘的接口都是一致的,键盘必须实现这一接口才能被电脑识别,那我们对Computer
和Keyboard
类进行修改。
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
| interface Board { public function type(); }
class CommonBoard implements Board { public function type(){ echo '普通键盘'; } }
class MechanicalKeyboard implements Board { public function type(){ echo '机械键盘'; } }
class Computer { protected $keyboard; public function __construct (Board $keyboard) { $this->keyboard = $keyboard; } }
$computer = new Computer(new MechanialKeyBoard());
|
可是这样也有问题,如果我们后续对这台电脑使用的键盘不满意要进行替换呢? 我们又回到原点了,必须去修改传入的键盘类。
能不能做成可配置的呢?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| class Container{ protected $binds; protected $instances; public function bind($abstract, $concrete) { if ($concrete instanceof Closure) { $this->binds[$abstract] = $concrete; } else { $this->instances[$abstract] = $concrete; } } public function make($abstract, $parameters = []) { if (isset($this->instances[$abstract])) { return $this->instances[$abstract]; }
array_unshift($parameters, $this); return call_user_func_array($this->binds[$abstract], $parameters); } }
|
这就是一个简单的IOC
服务容器。
这个怎么解决我们上述的问题呢?
1 2 3 4 5 6 7 8 9 10 11
| $container = new Container;
$container->bind('Board', function($container){ return new CommonBoard; });
$container->bind('Computer',function($container,$module){ return new Computer($container->make($module)); });
$computer = $container->make('Computer',['Board']);
|
这里生产出来的Computer
类就是一个使用普通键盘的电脑类了。
解释一下代码:
1 2 3 4 5 6 7 8
| bind(name,function($container){ return new Name; })
|
如果我们要更换键盘怎么办呢?
1 2 3 4 5 6 7 8 9
| $container->bind('Board', function($container){ return new MechanicalBoard; });
$container->bind('Computer',function($container,$module){ return new Computer($container->make($module)); });
$computer = $container->make('Computer',['Board']);
|
只要对bind
绑定的Board
类的实现进行修改,我们就可以很容易替换掉键盘了。这就是一个服务容器。
对服务容器进行一个理解:
容器就是一个装东西的,好比碗。而服务就是这个碗要装的饭呀,菜呀,等等东西。当我们需要饭时,我们就能从这个碗里拿到。如果你想在饭里加点菜(也就是饭依赖注入了菜),我们从碗里直接拿饭就可以了,而这些依赖都由容器解决了(这也就是控制反转)。
我们需要做的就是对提供的服务进行维护。
我们看一段真实的在laravel
框架上能跑的代码:代码
当然laravel
框架的服务容器比这里的要复杂很多了,但我们明白了它的使用目的以及使用场景就不难去入手laravel
了。