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