Contracts
Contracts
契約
介紹
Laravel 的契約是一組由框架提供,定義了核心服務的 interface 集合。比如, Illuminate\Contracts\Queue\Queue
契約定義了隊列任務所需方法,而 Illuminate\Contracts\Mail\Mailer
契約定義了發(fā)送郵件所需方法。
每個契約都擁有相應的框架提供的實現(xiàn)。比如,Laravel 提供了多種驅(qū)動的隊列實現(xiàn),并且使用 SwiftMailer 實現(xiàn)了郵件契約。
所有 Laravel 契約都在 它們各自的 GitHub 倉庫。這為所有可用的契約以及擴展包開發(fā)者們可能用到的單個、解耦的包,提供了一個快速參考入口。
契約 Vs. Facades
Laravel 的 Facades 和輔助函數(shù)提供了一種簡便方式來使用 Laravel 服務而無需用到類型提示,也可在服務容器外部解析契約。多數(shù)情況下,每個 Facade 都有一個等效的契約。
和 Facades (不須要在你類中的構(gòu)造函數(shù)去引用依賴)不同的是,契約允許你給自己的類定義明確的依賴。一些開發(fā)者更喜歡依賴被明確地定義出來,所以更傾向于使用契約,而其他開發(fā)者則享受于 Facades 帶來的方便。
{tip} 在大多數(shù)應用中,無論你更喜歡 Facades 還是契約,都是沒問題的。然而如果你在搭建擴展包,那你應該強烈考慮使用契約,因為他們更便于在包的上下文中做測試。
何時使用契約
綜上所述,使用契約還是 Facades 很大程度上取決于你個人或者團隊的喜好。契約和 Facades 均可以用來構(gòu)建健壯的、充分測試過的 Laravel 應用。只要你保持類的職責單一,你會發(fā)現(xiàn)使用契約和 Facades 的實際差別是非常小的。
然而,你以也許仍有許多關于契約的問題。比方說,為啥都用 interface ?用 interface 不是更復雜嗎?讓我們在接下來的內(nèi)容(「低耦合」與「簡明性」)中,提煉出原因。
低耦合
首先,讓我們來看一些緩存實現(xiàn)的高耦合代碼。假設有下面代碼:
<?php namespace App\Orders; class Repository{ /** * 緩存實例. */ protected $cache; /** * 創(chuàng)建一個新的倉庫實例. * * @param \SomePackage\Cache\Memcached $cache * @return void */ public function __construct(\SomePackage\Cache\Memcached $cache) { $this->cache = $cache; } /** * 根據(jù) ID 獲取訂單. * * @param int $id * @return Order */ public function find($id) { if ($this->cache->has($id)) { // } } }
在這個類中,代碼與給定的緩存實現(xiàn)形成高度耦合。它的高度耦合是因為我們依賴了一個擴展包中具體的緩存類。如果該擴展包的 API 變了,那么我們的代碼也將必須做出修改。
同理,如果我們想要將底層的緩存技術(shù) ( Memcached ) 替換成另一種緩存技術(shù) ( Redis ),我們得再次修改我們的代碼庫。我們的代碼庫不應該對誰提供的數(shù)據(jù)或者數(shù)據(jù)是怎么提供的有太多了解。
我們可以通過依賴一個簡單的與擴展包無關的 interface 來改進我們的代碼,來替代之前的實現(xiàn)方式:
<?php namespace App\Orders; use Illuminate\Contracts\Cache\Repository as Cache; class Repository{ /** * 緩存實例. */ protected $cache; /** * 創(chuàng)建一個新的倉庫實例. * * @param Cache $cache * @return void */ public function __construct(Cache $cache) { $this->cache = $cache; } }
現(xiàn)在的代碼不與任何特定的擴展包耦合了,甚至與 Laravel 都是無關的。由于契約擴展包不包含任何實現(xiàn)和依賴,你可以輕松地為給定的契約編寫替代實現(xiàn)的代碼,從而可以在不修改任何緩存代碼的情況下替換緩存的實現(xiàn)。
簡單
當 Laravel 的所有服務都在簡單的接口中整齊定義時,很容易通過給定的服務確定提供的功能。契約充當框架功能的簡潔性文檔。
此外,當你依賴簡單的接口時,你的代碼更容易理解和維護。你可以參考一個簡單,干凈的接口,而不是在大型復雜的的類中跟蹤哪些方法是可用的。
如何使用契約
那么,你如何去實現(xiàn)一個契約呢?它實際上很簡單。
Laravel 中的許多類型的類通過 服務容器 來解析,包括控制器,事件偵聽,中間件,隊列作業(yè),甚至路由閉包等。那么,要獲取一個合同的實現(xiàn),你只需在要解析的類的構(gòu)造方法中鍵入『類型提示』的接口。
例如,看看這個事件偵聽器:
<?php namespace App\Listeners; use App\User;use App\Events\OrderWasPlaced; use Illuminate\Contracts\Redis\Factory; class CacheOrderInformation{ /** * Redis 工廠實現(xiàn)。 */ protected $redis; /** * 創(chuàng)建一個新的事件處理器實例。 * * @param Factory $redis * @return void */ public function __construct(Factory $redis) { $this->redis = $redis; } /** * 處理事件。 * * @param OrderWasPlaced $event * @return void */ public function handle(OrderWasPlaced $event) { // } }
當事件偵聽器被解析時,服務容器將讀取類的構(gòu)造方法上的類型提示,并注入合適的值。要了解更多有關在服務容器中注冊的內(nèi)容,查看 服務容器文檔。
合約參考
此表提供了所有 Laravel 契約及其等效 facades 的一個快速參考: