Laravel深刻學習2 - 控制反轉容器

聲明:本文並不是博主原創,而是來自對《Laravel 4 From Apprentice to Artisan》閱讀的翻譯和理解,固然也不是原汁原味的翻譯,能保證90%的原汁性,另外由於是理解翻譯,確定會有錯誤的地方,歡迎指正。git

歡迎轉載,轉載請註明出處,謝謝!github

控制反轉容器

基礎綁定

上一張,咱們學習了依賴注入,接下來,咱們繼續探索的是「控制反轉」或者叫「依賴倒置」。後面咱們使用IoC容器來代指如上的定義。IoC容器使類的依賴管理變的很是方便,Laravel的核心就是由這種強大的容器思想來驅動的。IoC容器是Laravel框架重要的組成部分,他將框架中全部組件組織在一塊兒工做。事實上Laravel的Application類就是集成自Container容器類。設計模式

控制反轉容器(IoC Container)閉包

控制反轉使依賴注入變得更加便捷。若是在容器中定義好了相關的類或者接口(約定),咱們如何在程序中解析、並注入這些對象呢?框架

Laravel應用中,IoC容器可使用依照門面設計模式實現的App類來訪問容器。容器中包含了各式各樣的方法,這裏咱們只介紹一些比較基礎的方法。讓咱們以上一章中的BillerInterfaceBillingNotifierInterface爲基礎,來繼續探討使用Stripe1實現的支付功能。咱們能夠將Stripe的接口實現按照以下代碼綁定到容器中:函數

App::bind('BillerInterface', function()
{
    return new StripeBiller(App::make('BillingNotifierInterface'));
})

注意這裏,咱們在綁定了BillerInterface的同時,也注入了BillingNotifierInterface接口實現的具體類,因此要將接口綁定到容器中:學習

App::bind('BillingNotifierInterface', function()
{
    return new EmailBillingNotifier;
});

如上,咱們能夠理解,容器就是各類接口對應實現類的綁定的地方。一旦他們綁定到容器中,咱們就能在整個應用中的任意地方解析並使用他。咱們甚至能夠在解析器中繼續將其餘內容綁定到容器。this

有瑕疵?編碼

Laravel控制反轉容器是Fabien Potencier實現的Pimple2控制翻轉容器的一種替代方案。若是你已經在項目中使用到Pimple,儘可安心的升級爲Illuminate Container3組件,他爲您提供了更多好用的特性!翻譯

一旦使用了容器,切換接口實現就是一件很是簡單的事情,簡單到一行代碼就能搞定:

class UserController extends BaseController{

    public function __construct(BillerInterface $biller)
    {
        $this->biller = $biller;
    }
}

控制器經過容器實例化後,包含EmailBillingNotifierStripeBiller類就會隨着容器注入到控制器中。如今,若想更換通知器的實現方式,只須要接口綁定的實現便可:

App::bind('BillingNotifierInterface', function()
{
    return new SmsBillingNotifier;
});

如今,無需擔憂項目中到底哪裏用到了這個通知器,只需實現新的SmsBillingNotifier類便可。利用這種方式,咱們的應用能夠在不一樣的場景下實現快速切換。

是否是感受這種切換實現的方式很高大上。想象一下,若是想將短信通知器的服務提供商更換爲Twilio。咱們只須開發一個使用Twilio通知的實現類,並替換掉綁定到容器中的接口對應的實現類就好。若是在向Twilio的過分中出現了問題,咱們還能快速的將容器中接口綁定的類替換回原來的服務,這裏只須要那麼一丁點改變就能快速實現需求變動。能夠看到,依賴注入的優勢是超乎想象的。再多幾個例子?

好吧!接着往下看。 有時候,咱們想在整個應用中對一個類只進行一次解析,一次實例化。使用容器中的singleton方法便可:

App::singleton('BillingNotifierInterface', function()
{
    return new SmsBillingNotifier;
});

如今,容器一蛋解析了訂單通知類,在接下來的整個請求中都會使用同一個已實例化的實例。

容器中的intance方法和singleton有點相似;區別在於你能夠傳入一個已存在的對象來更新接口綁定的實例,在後續的使用中,容器都將使用到這個新的對象。

$notifier = new SmsBillingNotifier;
App::instance('BillingNotifierInterface', $notifier);

如今咱們已經熟悉使用容器進行閉包回調的基礎方法,接下來,讓咱們深刻挖掘下他更強大的功能:反射。

容器的獨立使用

即便沒有使用Laravel框架,咱們仍然能夠在項目中使用Composer安裝illuminate/container組件來使用Laravel的控制反轉容器。

反射

Laravel容器的一個強大的特性就是能經過反射自動解析依賴。反射具備檢測類及其方法的能力。好比,PHP中的ReflectionClass類容許你檢測一些方法在給定的類中是否可用。PHP函數method_exists也是反射的一種形式。看看下面的代碼,讓咱們來把玩一下:

$reflection = new ReflectionClass('StripeBiller');

var_dump($reflection->getMethods());

var_dump($reflection->getConstants());

經過使用PHP的這種特性,Laravel能夠實現一些有趣的功能!例如,以下代碼:

class UserController extends BaseController {

    public function __construct(StripeBiller $biller)
    {
        $this->biller = $biller;
    }

}

如上控制器初始化時須要傳入StripeBiller類型的對象,咱們能夠經過反射進行類型檢測。當Laravel容器沒有綁定相應的解析器,它就會經過反射嘗試解析該類。流程大體以下:

  1. 容器中有無StripeBiller解析器?

  2. 沒有解析器?映射類StripeBiller判斷其依賴。

  3. 遞歸的解析StripeBiller類的全部依賴。

  4. 經過ReflectionClass->newInstanceArgs()實例化一個新的StripeBiller

能夠看到,容器爲你作了不少繁重的工做,使你能釋放更多的時間用於編碼各類邏輯的代碼類。這就是Laravel容器特有的強大特性,也是它可以勝任構建大型應用的必殺器。

如今,咱們控制器中的代碼修改爲這樣,這會怎樣?

class UserController extends BaseController
{
    public function __construct(BillerInterface $biller)
    {
        $this->biller = $biller;
    }
}

假如咱們沒有對BillerInterface進行綁定,那麼容器如何注入其依賴的類呢?注意,接口只是個約定,他是不能進行實例化的。在沒有給定任何信息的狀況下,容器是沒法實例化相關依賴的。因此咱們須要使用bind方法來爲接口指定一個默認的類的實現:

App::bind('BillerInterface','StripBiller');

這裏咱們把字符串替換成一個閉包傳入容器,他會告訴容器任何狀況下老是使用StripeBiller這個實現自BillerInterface接口的類。這裏,咱們又能夠只修改一行代碼,就能進行替換掉容器中的綁定的邏輯了。假如咱們想使用餘額支付來代替現有的支付,咱們只須要完成繼承自BillerInterface接口的實現類BalanceInterfacez,同時修改下容器中的綁定:

App::bind('BillerInterface', 'BalancedBiller');

應用就會自動解析並使用這個新的支付方式。

一樣咱們也可使用singleton方法綁定接口,這樣在整個請求週期內容器只會進行一次實例化。

App::singleton('BillerInterface', 'StripeBiller');

掌握容器

想要深刻理解Laravel容器?那就通讀下代碼吧!容器只有一個類文件IlluminateContainerContainer。當你看完這個文件代碼後,確定能對容器有更深、更全面的認識。

          • -

2015-04-02 第二次閱讀修正。

2015-02-14 第一次翻譯發佈。

相關文章
相關標籤/搜索