DI
DI就是常說的依賴注入伴挚,那么究竟什么是依賴注入呢?
打個比方灾炭,電腦(非筆記本哈)需要鍵盤和鼠標(biāo)我們才能進(jìn)行操作,這個‘需要’換句話說就是‘依賴’鍵盤和鼠標(biāo)颅眶。
那么蜈出,相應(yīng)的,一個類需要另一個類才能進(jìn)行作業(yè)涛酗,那么這也就是依賴铡原。
看一段代碼:
class Computer {
protected $keyboard;
public function __construct() {
$this->$keyboard = new Keyboard();
}
}
這里的Computer類依賴了鍵盤類。
好商叹,既然我們已經(jīng)知道了什么是依賴燕刻,那么什么是注入呢?
我們改造一下上面的代碼:
class Computer {
protected $keyboard;
public function __construct(Keyboard $keyboard) {
$this->$keyboard = $keyboard;
}
}
$computer = new Computer(new Keyboard());
這里的Computer類依賴注入了Keyboard類剖笙。
關(guān)于依賴注入卵洗,我的理解是:
所需要的類通過參數(shù)的形式傳入的就是依賴注入。
理解了依賴注入弥咪,我們可以接著理解IOC过蹂。
IOC
IOC是什么呢?
中文叫控制反轉(zhuǎn)聚至。啥意思呢酷勺? 這個看明白了DI后就能很容易的理解了。
通過DI我們可以看到扳躬,一個類所需要的依賴類是由我們主動實例化后傳入類中的脆诉。
控制反轉(zhuǎn)和這個有什么關(guān)系呢?
控制反轉(zhuǎn)意思是說將依賴類的控制權(quán)交出去贷币,由主動變?yōu)楸粍印?/p>
看一段laravel代碼:
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class SessionController extends Controller
{
public function login(Request $request)
{
//這就是IOC击胜,我們不需要主動傳入類了一切由laravel去實現(xiàn)
}
}
看到這你可能有疑問了,這是怎么實現(xiàn)的呢役纹?
這就是靠服務(wù)容器了潜的,請往下接著看。
服務(wù)容器
看了很多文章字管,我一致認(rèn)為服務(wù)容器就是一種設(shè)計模式啰挪。
它的目的就是解耦依賴信不。
它有點類似于我前面說的《享元模式》。區(qū)別在于服務(wù)容器解決了所有依賴的實現(xiàn)抽活。
這里我們再從頭至尾的看一遍锰什,怎么一步步演化出服務(wù)容器。
依然是電腦的例子梭姓,我們知道電腦依賴鍵盤鼠標(biāo)誉尖,可是鍵盤鼠標(biāo)也有很多種呀铡恕。
先看一個最原始的代碼例子:
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;
}
}
}
或許你一眼就看出了問題在哪探熔。
如果我們又要增加一鐘鍵盤烘挫,那我們又得對這個類進(jìn)行修改饮六。這樣下去,這個類會變得龐大且耦合程度過高捉捅。
那么我們可以怎么修改呢虽风?
- 工廠模式
這樣我們可以避免直接的修改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);
}
}
這樣使用簡單工廠模式后厂抖,我們后續(xù)的修改可以不用對Computer類進(jìn)行操作而只要修改工廠類就行了。這就相當(dāng)于對Computer類進(jìn)行了解耦谭溉。
Computer類雖不在依賴那些鍵盤類了橡卤,但是卻變?yōu)橐蕾嚬S類了碧库。
后續(xù)添加新類型的鍵盤就必須對工廠類進(jìn)行修改嵌灰。
所以這個工廠類還不能很好的滿足要求,我們知道電腦對鍵盤的接口都是一致的迁匠,鍵盤必須實現(xiàn)這一接口才能被電腦識別,那我們對Computer和Keyboard類進(jìn)行修改吠架。
- 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());
可是這樣也有問題,如果我們后續(xù)對這臺電腦使用的鍵盤不滿意要進(jìn)行替換呢魂仍? 我們又回到原點了擦酌,必須去修改傳入的鍵盤類赊舶。
能不能做成可配置的呢?
- IOC服務(wù)容器(超級工廠)
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服務(wù)容器。
這個怎么解決我們上述的問題呢锌唾?
$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']);
這里生產(chǎn)出來的Computer類就是一個使用普通鍵盤的電腦類了滋捶。
解釋一下代碼:
bind(name,function($container){
return new Name;
})
這里的name和Name之間的關(guān)系是:
當(dāng)我需要name類的時候你就給我實例化Name類渐排。
make(name)方法是對name進(jìn)行生產(chǎn)返回一個實例亲族。
如果我們要更換鍵盤怎么辦呢霎迫?
$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類的實現(xiàn)進(jìn)行修改知给,我們就可以很容易替換掉鍵盤了涩赢。這就是一個服務(wù)容器筒扒。
對服務(wù)容器進(jìn)行一個理解:
容器就是一個裝東西的花墩,好比碗。而服務(wù)就是這個碗要裝的飯呀祠肥,菜呀搪柑,等等東西。當(dāng)我們需要飯時,我們就能從這個碗里拿到旬迹。如果你想在飯里加點菜(也就是飯依賴注入了菜)奔垦,我們從碗里直接拿飯就可以了屹耐,而這些依賴都由容器解決了(這也就是控制反轉(zhuǎn))。
我們需要做的就是對提供的服務(wù)進(jìn)行維護(hù)椿猎。
我們看一段真實的在laravel框架上能跑的代碼:
當(dāng)然laravel框架的服務(wù)容器比這里的要復(fù)雜很多了惶岭,但我們明白了它的使用目的以及使用場景就不難去入手laravel了。
PS: 知乎專欄 '程序邊緣',熱烈歡迎7该摺0丛睢!
個人訂閱號:Buger,為你收集各類學(xué)習(xí)資料