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 中文手册


