聲明:本文並不是博主原創,而是來自對《Laravel 4 From Apprentice to Artisan》閱讀的翻譯和理解,固然也不是原汁原味的翻譯,能保證90%的原汁性,另外由於是理解翻譯,確定會有錯誤的地方,歡迎指正。git
歡迎轉載,轉載請註明出處,謝謝!github
上一張,咱們學習了依賴注入,接下來,咱們繼續探索的是「控制反轉」或者叫「依賴倒置」。後面咱們使用IoC容器來代指如上的定義。IoC容器使類的依賴管理變的很是方便,Laravel的核心就是由這種強大的容器思想來驅動的。IoC容器是Laravel框架重要的組成部分,他將框架中全部組件組織在一塊兒工做。事實上Laravel的Application
類就是集成自Container
容器類。設計模式
控制反轉容器(IoC Container)閉包
控制反轉使依賴注入變得更加便捷。若是在容器中定義好了相關的類或者接口(約定),咱們如何在程序中解析、並注入這些對象呢?框架
Laravel應用中,IoC容器可使用依照門面設計模式實現的App
類來訪問容器。容器中包含了各式各樣的方法,這裏咱們只介紹一些比較基礎的方法。讓咱們以上一章中的BillerInterface
和BillingNotifierInterface
爲基礎,來繼續探討使用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; } }
控制器經過容器實例化後,包含EmailBillingNotifier
的StripeBiller
類就會隨着容器注入到控制器中。如今,若想更換通知器的實現方式,只須要接口綁定的實現便可:
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容器沒有綁定相應的解析器,它就會經過反射嘗試解析該類。流程大體以下:
容器中有無StripeBiller解析器?
沒有解析器?映射類StripeBiller
判斷其依賴。
遞歸的解析StripeBiller
類的全部依賴。
經過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 第一次翻譯發佈。