Contracts
介紹
Laravel 的 Contracts 是一組定義了框架核心服務的介面。例如,Illuminate\Contracts\Queue\Queue
contract 定義了隊列任務所需要的方法,而 Illuminate\Contracts\Mail\Mailer
contract 定義了寄送 e-mail 需要的方法。
框架對於每個 contract 都有提供對應的實作,例如,Laravel 提供各種驅動程式的隊列實作,以及由 SwiftMailer 提供的 mailer 實作。
Laravel 所有的 contracts 都放在各自的 GitHub 儲存庫。除了提供所有可用的 contracts 一個快速的參考,也可以單獨作為一個鬆散耦合的套件讓其他套件開發者使用。
Contracts Vs. Facades
Laravel 的 facades 和輔助方法提供一個簡單的方法來使用服務,而不需要使用型別提示和在服務容器之外解析 contracts。在大部分情況下,facade 有等價的 contract。
與 facades 不同,contracts 不會要求你在類別建構子內引入,它讓你定義類別明確的依賴。有些開發者偏好明確定義它們的依賴關係,因此使用 contracts,而有些開發者則享受 facades 的便利性。
{tip} 大部分的程式不介意你使用的是 facades 或 contracts。然而,如果你正在開發套件的話,強烈建議使用 contracts,它在你的套件中將更容易地被測試。
何時使用 Contracts
如上所討論的,大部分決定使用 facades 或 contracts 取決於個人或開發團隊的喜好。facades 或 contracts 這兩者都可以建立強健的、好測試的 Laravel 應用程式。只要保持的類別專注單一職責,你會發現使用 facades 或 contracts 沒有多大的實質差別。
然而,你可能對於 contracts 仍舊有許多的疑惑。例如,為何全部使用介面?使用介面的話不是更複雜嗎?讓我們用下面的論點來解釋使用介面的原因:鬆散耦合和簡單性。
鬆散耦合
讓我們來檢視這一段和快取功能有緊密耦合的程式碼。思考以下程式碼:
<?php
namespace App\Orders;
class Repository
{
/**
* 快取實例。
*/
protected $cache;
/**
* 建立新 repository 實例。
*
* @param \SomePackage\Cache\Memcached $cache
* @return void
*/
public function __construct(\SomePackage\Cache\Memcached $cache)
{
$this->cache = $cache;
}
/**
* 透過 ID 取得訂單。
*
* @param int $id
* @return Order
*/
public function find($id)
{
if ($this->cache->has($id)) {
//
}
}
}
在這個類別中,程式和給定的快取實作有緊密耦合,因為我們從套件 vendor 依賴一個具體的快取類別。
同樣的,如果想要將底層的快取技術(Memcached)抽換成另一種(Redis),我們需要再次修改我們的 Repository。我們的 Repository 不應該知道由誰提供了資料,或是如何提供的細節。
比起以上的做法,我們可以透過一個簡單、和套件無關的程式碼來改善我們的程式碼:
<?php
namespace App\Orders;
use Illuminate\Contracts\Cache\Repository as Cache;
class Repository
{
/**
* 快取實例。
*/
protected $cache;
/**
* 建立新 repository 實例。
*
* @param Cache $cache
* @return void
*/
public function __construct(Cache $cache)
{
$this->cache = $cache;
}
}
現在上面的程式碼沒有跟任何套件耦合,甚至是 Laravel。既然 contracts 套件沒有包含實作和任何依賴,你可以很簡單的對任何 contract 進行實作,你可以很簡單的寫一個替換的實作,甚至是替換 contracts,讓你可以替換快取實作而不用修改任何用到快取的程式碼。
簡單性
當所有的 Laravel 服務都簡潔的使用簡單的介面定義,就能夠很簡單的決定一個服務需要提供的功能。可以將 contracts 視為說明框架特色的簡潔文件。
除此之外,當你依賴簡潔的介面,你的程式碼能夠很簡單的被瞭解和維護。比起搜尋一個大型複雜的類別裡有哪些可用的方法,你有一個簡單,乾淨的介面可以參考。
如何使用 Contracts
所以,要如何實作一個 contract 呢?實際上非常的簡單。
很多 Laravel 的類別都是經由服務容器來解析,包含控制器,事件監聽,中介層,隊列任務,甚至是路由閉包。所以,要實作一個 contract,你可以在類別的建構子使用「型別提示」解析類別。
例如,我們來看看這個事件監聽程式:
<?php
namespace App\Listeners;
use App\User;
use App\Events\OrderWasPlaced;
use Illuminate\Contracts\Redis\Database;
class CacheOrderInformation
{
/**
* Redis 資料庫的實作。
*/
protected $redis;
/**
* 建立新事件處理器實例。
*
* @param Database $redis
* @return void
*/
public function __construct(Database $redis)
{
$this->redis = $redis;
}
/**
* 處理事件。
*
* @param OrderWasPlaced $event
* @return void
*/
public function handle(OrderWasPlaced $event)
{
//
}
}
當事件監聽被解析時,服務容器會經由類別建構子參數的型別提示,注入適當的值。要知道怎麼註冊更多服務容器,參考這份文件。
Contract 的參考清單
下表是 Laravel Contracts 的快速參考,以及其對應的 facade: