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