1. 程式人生 > >詳解Laravel依賴注入(DI)和Ioc容器

詳解Laravel依賴注入(DI)和Ioc容器

需要了解更多laravel的學習知識,請訪問我的小站:http://laravelbase.com/

Author:Woody

Laravel這個框架,用起來方便,理解起來不簡單。

為什麼不簡單?因為包含了一大堆所謂“先進”的概念,其中依賴注入(DI)和Ioc容器是比較核心的內容之一。

我百度了一下,講PHP DI和Ioc的內容很少,更別說詳解Laravel ioc的了。

在這裡,我綜合了幾篇寫得比較典型的文章,以一個產品經理的身份,從使用者體驗的角度嘗試讓初學者也能比較容易理解這個2個概念。

DI和Ioc就是一種設計模式,這個概念已經存在10幾年了,請先對面向物件程式設計和設計模式(DesignPatterns)有初步的理解:

預先了解一下這三種模式:1. 工廠模式 2. 單例模式 3. 註冊器模式

也請了解一下 interface 的用法和作用;

如果上述內容你都不是很熟悉,沒關係,很簡單的,請接著看:

1. 首先來看 依賴注入(DI):

故事是這樣的:

我們知道寫一個類的時候,類本身是有個目的的,類裡面有很多方法,每個方法搞定一些事情;

class Hookup {

public function getPhone(){

     //此處省略

}

public function inviteHer(){

     //此處省略

}

public function giveGifts(){

     //此處省略

}

public function kissHer(){

     //此處省略

}

}

比方說

這個類是什麼意思?Hookup就是泡妞的意思,裡面有4個步驟,第一步打電話,第二部邀請她共進晚餐,第三步送禮物,第四步打波兒,第五步.....我只能幫你到這裡了...

別看只有4步,其實每一步都不簡單,第一步打電話,你得先要到人家電話吧,第二步邀請吃飯,你得提前訂好飯店吧,第三步送禮物,你得先買禮物吧~第四步Kiss,你總得抓住一個合適的機會吧;

當你在專心處理Hookup的時候,我太瞭解你了,你只關心結果,也就是抱得美人歸,問電話號碼,訂餐,買禮物,找機會這種小事,就交給其他“類”處理吧。

如下:

 require 'BuyGifts.php';

class Hookup {

public function getPhone(){

     //此處省略

}

public function inviteHer(){

     //此處省略

}

public function giveGifts(){

$gift->select();

}

}

}

$paoniu = new hookup();

$paoniu->giveGifts();

這裡面送禮物這個環節(比較懶,其他的不寫了),就引入了BuyGifts這個類,買禮物這種小事,就交給BuyGifts這個祕書去處理吧。

所以,一切問題的出發點,就是在一個類中呼叫其他類,我們要解決的,就是在這過程中會發生的問題。

Hookup這個可以稱之為主類,BuyGifts稱之為次類,為了實現主類的目標,要依賴很多次類來配合。

好,現在你已經知道什麼是依賴了。

問題來了。現在講正經的。

比方說你很忙,你不僅要泡妞,還要開公司,做生意,沒事打打高爾夫球什麼的。這每一件事都是一個“主類”,BuyGifts 這種“次類” ,除了泡妞,也可以放在 做生意這個類裡面呼叫,做生意也要送禮的嘛,當然還可以應用在別的主類。 

如果BuyGifts被呼叫很多次,哪一天需要把名字改了,那你就必須在眾多主類中逐一修改。

還有就是,每一次呼叫都要new例項化一次。

這樣做不科學,你也知道。

怎麼辦: 工廠模式

工廠模式很簡單,就是來幫助你批量例項化“次類”的。也就是說,用了工廠模式,你的主類中將不會再出現new這樣的關鍵字。

看例項,有3個類 哦:

-----   Factory.php    ----------

include 'BuyGifts.php'

class Factory {

static function getPhone(){

return new getPhone;

}

static function BuyGifts(){

return new BuyGifts;

}

//……下面還有很多,此處省略

}

-----   BuyGifts.php    ----------

class  BuyGifts{

public function select(){

//此處省略 }

public function pay(){

//此處省略 }

}

-----   Hookup.php    ----------

require 'BuyGifts.php';

require 'Factory.php ';

class Hookup {

private $gift;

public function getPhone(){

     //此處省略

}

public function inviteHer(){

     //此處省略

}

public function giveGifts(){

$gift->select();

}

}

}

$paoniu = new hookup();

$paoniu->giveGifts();

你看,現在主類Hookup 要呼叫次類BuyGifts,就得先去找Factory類,Factory就變成中介了。

Factory這個中介的主要服務就是幫 你例項化次類,另外管理很多次類(工廠嘛),這樣你就不用直接與次類發生關係。

這個過程就叫做 解耦,不管次類怎麼變,你找工廠就可以了。

可是這樣做問題依舊存在,,當我們在很多主類裡呼叫了工廠及其方法,可是有一天發現工廠類要改名,,或者工廠裡面的方法要改名呢?那我們還不是得逐一改主類?雖然這種情況不多,但是這種不講信譽的“黑中介”也是偶爾會出現的。

怎麼辦呢?我們對這個Factory 中介 也得 防 一手。

-----   Hookup.php    ----------

require 'BuyGifts.php';

require 'Factory.php ';

class Hookup {

private $gift;

public function getPhone(){

     //此處省略

}

public function inviteHer(){

     //此處省略

}

public function giveGifts(){

$gift->select();

$gift->pay();

}

public function kissHer(){

     //此處省略

}

public function setGift($instance){

$this->gift = $instance;

}

}

$paoniu = new hookup();

$paoniu->giveGifts();

現在Hookup類就像一個公司,作為老闆的你只關心怎麼泡妞,髒活累活交給別人幹,於是你設立了一個setGift採購部門,這個部門專門負責和Factory中介打交道,這樣Factory中介就完全滾粗你的視野了。

Factory這個被依賴的物件 是通過引數 從外部 注入到 類內容的 靜態 屬性 實現例項化的,這個就是依賴注入。

可以聰明的你馬上發現,我擦,setGift 你這個部門就負責採購gift啊?我要打電話,發預約邀請難道還要setPhone,setInvitation嗎?

這樣主類裡面要寫很多set方法,外面要呼叫很多次set方法,你會發現,更不科學了。

裡面:

public function setGift($instance){

}

$this->phone = $instance;

}

public function setInvitation($instance){

$this->Invitation = $instance;

}

….

外面:

$paoniu->setGift(Factory::BuyGifts());

$paoniu->setPhone(Factory::GetPhone());

$paoniu->setInvitation(Factory::SendInvitation());

….

這個時候,你已經把Factory 趕出 Hookup公司了,所以,公司內部的事情,只能你自己搞定了。

公司外部的業務,這時Factory中介 又屁顛屁顛的跑過來找你,提出一個方案:

我們來搞個“總代”怎麼樣?

class TopFactory {

public static function all_Agents_in_one(){

          $paoniu_agent->setGift(Factory::BuyGifts()); 

         $paoniu_agent->setInvitation(Factory::SendInvitation());

return $paoniu_agent;

}

}

黑中介Factory對你說:“你看我搞了個總代公司(TopFactory),你在外面也不要new啊,set什麼的了,我全包了。

於是現在你在外面要做的變簡單了:

$paoniu = TopFactory::all_Agents_in_one();

$paoniu->giveGifts();//giveGifts裡面就可以呼叫Buygifts了;

$paoniu->getPhone();

….

到現在為止,你已經有一套完整的 依賴注入方案了,這個方案組建的時候雖然有點複雜(層層轉包),但是一旦建好,維護起來比較方便。

可是,別忘了公司內部的事情還沒解決哪,另外要提醒你,你是程式設計師,不是老闆,所以Factory那一部分也是你的活~~~

需要一種更高階的方案,讓程式設計師的生活變得Easier一些。

這個方案就是IoC容器,IoC容器首先是一種類註冊器,其次它是一種更高階的依賴注入方式。

它和工廠Factory其實性質一樣,都是中介代理,但實現機制不一樣。

工廠Factory 把 次類 一一對應 註冊到 類中的 例項化靜態方法中;

IoC容器是把 次類 例項化物件 依次 註冊到 類中一個靜態陣列;

IoC容器的設計模式叫做 註冊器模式;

看例項:

———Di.php——-

class Di {

protected static $objects;

self::$objects[$key]=$object;

}

static function get ($key){

}

unset(self::$objects[$key]);

}

}

-----   Hookup.php    ----------

require ‘BuyGifts.php';

require 'Factory.php ';

require 'Di.php ';

class Hookup {

private $di;

       $this->_di = $di;

    }

public function getPhone(){

     //此處省略

}

public function inviteHer(){

     //此處省略

}

public function giveGifts(){

$this->di->get(‘BuyGifts’)->select();

$this->di->get(‘BuyGifts’)->pay();

}

public function kissHer(){

     //此處省略

}

public function setGift($instance){

$this->gift = $instance;

}

}

$di = new Di();

}

$paoniu=new Hookup($di);

$paoniu->giveGifts();

———-

可以看到,IoC模式才是真正的總代,它連公司內部的事情也管了。和設立set部門相比,set只能處理一個個單獨的業務,比如setGift,setPhone,setInvitation,之類的,,而ioc容器把這些例項全部放在一個數組裡統一處理,並且還可以重新命名(鍵名),實現了完全的解耦。

請注意的是,我們在註冊Ioc容器的時候,是這樣寫的:

}

$di->set(‘BuyGift’, Factory::BuyGift());

第一個引數是陣列的鍵名,第二個引數是陣列的值;

第一種寫法用了回撥函式,它只有在讀取陣列的值的時候才會被執行;

第二種方法直接例項化成一個物件,儲存到陣列的值中。第二種方法會比較佔資源。

另外我們發現

$di->set(‘BuyGift’, function(){

return Factory::BuyGift();

}

$paoniu=factory::Hookup(factory::DI()); // 將容器注入主類

$paoniu->giveGifts(); //這裡就可以呼叫BuyGift()這個次類(依賴類);

好了,我們來看看Laravel Ioc容器 是怎麼用的:

繫結類到容器

App::bind('foo', function()

{

    return newFooBar;

});

App 就是Ioc容器,bind就是set方法,’foo’ 就是陣列中 例項的 鍵名;

至此,我們已經對Ioc容器有了一個比較清楚的認識,我們已經瞭解了工廠模式,註冊模式,依賴注入等知識,其實依賴注入有3種方式,,一種就是set注入(單類注入),一種是容器注入(Ioc),還有一種是介面注入(Interface);前兩種我們已經學習過了,後一種放在本文的第二部分來講。

在本文的第二部分,我們將詳細講解LaravelIoC的用法,為什麼說Laravel理解起來比較難,因為它的設計不是入門級別的,而是專業級別的,比前面講的要複雜許多,但是看官方文件,寥寥數語,真有種想砸電腦的衝動。