Laravel 的合同(Contracts)就是在框架里定义核心服务的一些接口。比如,一个 Queue 合同定义了队列工作需要用到的一些方法,Mailer 合同里边定义了发送邮件要用的一些方法。
框架为每个合同都提供了一个对应的实施。比如,Laravel 提供了一个 Queue 实施,里面有各种驱动,Mailer 实施里面用的是 SwiftMailer 。
所有的 Laravel 合同都在各种的 Github 仓库里。这为所有可用的合同,都提供了一个可以快速查看参考的地方,这样其它的开发者可以使用这些独立的,解耦的包。
为什么要用合同
你可能会有些疑问,为什么要用合同,使用接口干啥,这不是更复杂吗?下面介绍两个主要的原因,松耦合与简洁。
松耦合
先来看点实施缓存的紧耦合的代码:
<?php namespace App\Orders; class Repository { /** * The cache. */ protected $cache; /** * Create a new repository instance. * * @param \Package\Cache\Memcached $cache * @return void */ public function __construct(\SomePackage\Cache\Memcached $cache) { $this->cache = $cache; } /** * Retrieve an Order by ID. * * @param int $id * @return Order */ public function find($id) { if ($this->cache->has($id)) { // } } }
在上面这个类里,代码被紧密地耦合到了一个给定的缓存实施上,之所以被称为是紧耦合,是因为我们依赖来自一个包里的一个固定的缓存类,如果这个包里的 API 改变了,我们自己的代码也需要改变。
同样,如果我们需要更换使用的缓存 ,比如把 Memcached 换成 Redis,这同样需要去修改我们自己的代码。我们的代码应该不需要知道是谁给它提供的数据,或者怎么提供的这些数据。
下面使用接口去改进一下:
<?php namespace App\Orders;
use Illuminate\Contracts\Cache\Repository as Cache;
class Repository {
/**
* Create a new repository instance.
*
* @param Cache $cache
* @return void
*/
public function __construct(Cache $cache)
{
$this->cache = $cache;
}
}
现在,代码不再跟一个特定的 vendor 绑定到一块儿了,甚至跟 Laravel 也一样。合同包里不包含实施也没有依赖,你可以简单的为任何合同去写一个替代的实施,这样你可以在不通知任何使用缓存的代码的情况下,替换掉你的缓存实施。
简洁
所有 Laravel 的服务都是用的简单的接口定义的,这样就很容易确定哪些功能来自哪些服务。合同的作用就是为架构的功能提供一个简洁的文档。别把方法放到一个巨大又复杂的类里面,使用简单,整洁的接口。这样你的代码就会很容易明白与维护。
合同参考
下面是 Laravel 里的大部分合同,还有跟它们对应的 facade。
如何创建合同
怎么样去实施合同?在 Laravel 里面的大部分类都是通过 Service Container 去 resolved ,包括控制器,事件监听器,过滤器,队列工作,甚至 Closures 路由。要去实施一个合同,你只需要在要 resolved 类的构造函数里面去 type-hint 接口。比如,看一下,下面这个 event handler :
<?php namespace App\Handlers\Events; use App\User; use App\Events\NewUserRegistered; use Illuminate\Contracts\Redis\Database; class CacheUserInformation { /** * The Redis database implementation. */ protected $redis; /** * Create a new event handler instance. * * @param Database $redis * @return void */ public function __construct(Database $redis) { $this->redis = $redis; } /** * Handle the event. * * @param NewUserRegistered $event * @return void */ public function handle(NewUserRegistered $event) { // } }
当事件监听器被 resolved 以后,Service Container 会去读取在类的构造函数上的 type-hint ,然后会注入适当的值。了解怎么样在 Service Container 上注册东西,可以查看这个文档。
Laravel Laravel5 中文手册