[TOC]
# DI
DI就是常說的依賴注入,那么究竟什么是依賴注入呢?
打個比方,電腦(非筆記本哈)需要鍵盤和鼠標我們才能進行操作,這個‘需要’換句話說就是‘依賴’鍵盤和鼠標。
那么,相應的,一個類需要另一個類才能進行作業,那么這也就是依賴。
看一段代碼:
```
class Computer {
protected $keyboard;
public function __construct() {
$this->$keyboard = new Keyboard();
}
}
這里的Computer類依賴了鍵盤類。
```
好,既然我們已經知道了什么是依賴,那么什么是注入呢?
我們改造一下上面的代碼:
```
class Computer {
protected $keyboard;
public function __construct(Keyboard $keyboard) {
$this->$keyboard = $keyboard;
}
}
$computer = new Computer(new Keyboard());
這里的Computer類依賴注入了Keyboard類。
```
關于依賴注入,我的理解是:
> 所需要的類通過參數的形式傳入的就是依賴注入。
理解了依賴注入,我們可以接著理解IOC。
# IOC
IOC是什么呢?
中文叫控制反轉。啥意思呢? 這個看明白了DI后就能很容易的理解了。
通過DI我們可以看到,一個類所需要的依賴類是由我們主動實例化后傳入類中的。
控制反轉和這個有什么關系呢?
> 控制反轉意思是說將依賴類的控制權交出去,由主動變為被動。
看一段laravel代碼:
```
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class SessionController extends Controller
{
public function login(Request $request)
{
//這就是IOC,我們不需要主動傳入類了一切由laravel去實現
}
}
```
看到這你可能有疑問了,這是怎么實現的呢?
這就是靠服務容器了,請往下接著看。
# 服務容器
看了很多文章,我一致認為服務容器就是一種設計模式。
> 它的目的就是解耦依賴。
這里我們再從頭至尾的看一遍,怎么一步步演化出服務容器。
依然是電腦的例子,我們知道電腦依賴鍵盤鼠標,可是鍵盤鼠標也有很多種呀。
先看一個最原始的代碼例子:
```
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類。
```
簡單工廠
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類進行修改。
- DI(依賴注入)
```
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());
```
可是這樣也有問題,如果我們后續對這臺電腦使用的鍵盤不滿意要進行替換呢? 我們又回到原點了,必須去修改傳入的鍵盤類。
能不能做成可配置的呢?
- IOC服務容器(超級工廠)
```
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服務容器。
這個怎么解決我們上述的問題呢?
```
$container = new $container;
$container->bind('Board', function($container){
return new CommonBoard;
});
$computer->bind('Computer',function($container,$module){
return new Computer($container->make($module));
});
$computer = $container->make('Computer',['Board']);
```
這里生產出來的Computer類就是一個使用普通鍵盤的電腦類了。
解釋一下代碼:
```
bind(name,function($container){
return new Name;
})
這里的name和Name之間的關系是:
當我需要name類的時候你就給我實例化Name類。
make(name)方法是對name進行生產返回一個實例。
```
如果我們要更換鍵盤怎么辦呢?
```
$container->bind('Board', function($container){
return new MechanicalBoard;
});
$computer->bind('Computer',function($container,$module){
return new Computer($container->make($module));
});
$computer = $container->make('Computer',['Board']);
```
只要對bind綁定的Board類的實現進行修改,我們就可以很容易替換掉鍵盤了。這就是一個服務容器。
對服務容器進行一個理解:
> 容器就是一個裝東西的,好比碗。而服務就是這個碗要裝的飯呀,菜呀,等等東西。當我們需要飯時,我們就能從這個碗里拿到。如果你想在飯里加點菜(也就是飯依賴注入了菜),我們從碗里直接拿飯就可以了,而這些依賴都由容器解決了(這也就是控制反轉)。
我們需要做的就是對提供的服務進行維護。
我們看一段真實的在laravel框架上能跑的代碼:
[代碼](https://implode.io/4mT8O4)
當然laravel框架的服務容器比這里的要復雜很多了,但我們明白了它的使用目的以及使用場景就不難去入手laravel了。
PS: 個人微信公眾號'buger'。