Contracts
简介
Laravel 的 Contract 是一组用于定义框架提供的核心服务的接口。例如,Illuminate\Contracts\Queue\Queue
Contract 定义了队列任务所需的方法,而 Illuminate\Contracts\Mail\Mailer
Contract 定义了发送电子邮件所需的方法。
框架对每个 Contract 都提供了相应的实现。例如,Laravel 提供了带有各种驱动的队列实现,以及由 SwiftMailer 提供支持的邮件驱动实现。
所有的 Laravel Contract 都有它们自己的 GitHub 仓库。这为所有可用的 Contract 提供了一个快速参考指南,同时也可单独作为低耦合的扩展包供包开发者使用。
Contract Vs. Facade
Laravel 的 Facade 和辅助函数都提供了一种使用 Laravel 服务的简单方法,而无需使用类型提示或者从服务容器中解析 Contract。在大多数情况下,每个 Facade 都有一个等效的 Contract。
不像 Facade,Contract 不用在类的构造函数中使用类型提示,而是在类中明确地定义依赖。有些开发者更喜欢用 Contract 这种方式明确地定义它们的依赖,而其他开发者更喜欢 Facade 带来的便利。
对于大多数应用来说,不管是使用 Facade 还是 Contract 都可以。但是,如果要构建一个扩展包,则强烈推荐使用 Contract,因为它们更容易在扩展包中结合上下文进行测试。
什么时候使用 Contract
综上所述,使用 Contract 或 Facade 很大程度上取决于个人或者开发团队的喜好。不管是 Contract 还是 Facade 都可以创建出强大的、易于测试的 Laravel 应用。只要您始终保持类的单一职责,就会发现使用 Contract 和 Facade 实际上差异很小。
但是,您可能仍有几个关于 Contract 的问题。例如,为什么要使用接口?不使用接口会更复杂吗?下面让我们谈谈使用接口的原因:低耦合和简单性。
低耦合
首先,让我们来看一些高耦合缓存实现的代码。如下:
namespace App\Orders;
class Repository
{
/**
* 缓存实例
*/
protected $cache;
/**
* 创建一个缓存库实例
*
* @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)) {
//
}
}
}
在此类中,代码和给定的缓存实现紧密耦合。高耦合是因为我们依赖于一个扩展包的特定缓存类。一旦这个扩展包的 API 发生更改,我们的代码也必须更改。
同样的,如果我们想要将底层的的缓存技术(Memcached)替换为另一种缓存技术(Redis),我们又得再次修改缓存库。但我们的缓存库不应该知道谁为它们提供了数据或是它们如何提供数据。
除了这种方法之外,我们还可以通过使用一个简单的、与供应商无关的接口来改进我们的代码:
namespace App\Orders;
use Illuminate\Contracts\Cache\Repository as Cache;
class Repository
{
/**
* 缓存实例
*/
protected $cache;
/**
* 创建一个缓存实例
*
* @param Cache $cache
* @return void
*/
public function __construct(Cache $cache)
{
$this->cache = $cache;
}
}
现在的代码没有和任何扩展包,甚至与 Laravel 相耦合。由于 Contract 扩展包不包含任何实现并且没有依赖,因此您可以轻松编写任何替代实现,从而允许您替换缓存实现而不用修改任何使用缓存的代码。
简单性
当所有 Laravel 的服务都使用简单的接口定义时,就很容易判断给定服务提供的功能。Contract 就是框架功能的简易文档。
此外,当依赖简单的接口时,您的代码会更易于理解和维护。比起在一个大型复杂的类中搜索有哪些可用的方法,不如参考一个简单干净的接口。
如何使用 Contract
那么,如何获取一个 Contract 的实现呢?实际上很简单。
Laravel 中的许多类都通过 服务容器 解析,包括控制器、事件监听器、中间件、任务队列,甚至路由闭包。因此,要获得一个 Contract 的实现,只需要在被解析的类的构造函数中添加「类型提示」即可。
例如,看看这个事件监听器:
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 Contract 及对应 Facade 的快速参考: