契约

简介

Laravel 的契约是指框架提供的一系列定义核心服务的接口。例如 Illuminate\Contracts\Queue\Queue 契约定义了队列任务需要实现的方法,而 Illuminate\Contracts\Mail\Mailer 契约定义了发送邮件所需要实现的方法。

每一个契约都有框架提供相应的实现。例如 Laravel 为队列提供了多个驱动的实现,邮件则由 SwiftMailer驱动实现。

所有的契约都有其 对应的 GitHub 仓库。这为所有可用的契约提供了一个快速入门的指南,同时也可以单独作为低耦合的扩展包被开发者使用。

契约 Vs. Facades

Laravel 的 facades 和辅助函数都提供了一种使用 Laravel 服务的简单方法,既不需类型提示,也不需要从服务容器中解析契约。大多情况下,每一个 Facades 都有一个等效的契约。

在 Facades 中,你不需要在构造函数中做类型提示,但是契约需要你在类中明确的定义依赖。一些开发者倾向于使用契约这种明确定义依赖的方式,而其他开发者则更喜欢 Facades 带来的便捷。

{tip} 对于大多数的应用程序而言,不管是使用 Facades 还是契约,都可以。但是如果你在构建一个扩展包,为了方便测试,强烈建议你使用契约

何时使用契约

综上所述,使用契约还是 Facades 很大程度上取决于你个人或者团队的喜好。两者都可以使创建强大的、测试友好的 Laravel 应用程序。只要你保持类的职责单一,你会发现使用契约还是 Facades,其实并没有什么实质性的区别。

但是,你可能还是会对契约有些困惑。比如,为什么要全部使用接口?使用接口会不会更复杂?下面让我们从两个方面来聊一聊为什么使用接口:低耦合和简单性。

低耦合

首先,让我们来看一些缓存实现的高耦合代码:

  1. <?php
  2. namespace App\Orders;
  3. class Repository
  4. {
  5. /**
  6. * 缓存实例
  7. */
  8. protected $cache;
  9. /**
  10. * 创建一个仓库实例
  11. *
  12. * @param \SomePackage\Cache\Memcached $cache
  13. * @return void
  14. */
  15. public function __construct(\SomePackage\Cache\Memcached $cache)
  16. {
  17. $this->cache = $cache;
  18. }
  19. /**
  20. * 按照 Id 获取订单
  21. *
  22. * @param int $id
  23. * @return Order
  24. */
  25. public function find($id)
  26. {
  27. if ($this->cache->has($id)) {
  28. //
  29. }
  30. }
  31. }

在这个类中。由于我们基于一个来自包的具体的缓存类,代码和给定的缓存实现了紧密耦合。但是,如果包的 API 变了,那么相应的,我们的代码必须做修改。

同理,如果我们想要将底层的缓存技术 (Memcached) 替换成另一种缓存技术 (Redis),我们得再次修改我们的代码库。但其实是我们的代码库并不需要知道谁提供的数据或者数据是怎么提供的。

我们可以基于一种简单的、与扩展包无关的接口来优化我们的代码,从而替代上面的那种实现方式:

  1. <?php
  2. namespace App\Orders;
  3. use Illuminate\Contracts\Cache\Repository as Cache;
  4. class Repository
  5. {
  6. /**
  7. * 缓存实例
  8. */
  9. protected $cache;
  10. /**
  11. * 创建一个仓库实例
  12. *
  13. * @param Cache $cache
  14. * @return void
  15. */
  16. public function __construct(Cache $cache)
  17. {
  18. $this->cache = $cache;
  19. }
  20. }

现在,更改之后的代码就不与任何特定的扩展包耦合,甚至与 Laravel 都是无关的。由于契约扩展包不包含任何实现和依赖,你可以轻松的为给定的契约编写替代实现的代码,从而实现在不需要修改任何缓存代码的情况下随意替换缓存的实现。

简单性

当所有的 Laravel 服务都统一使用简单接口定义时,就会很容易判断给定服务提供的功能。可以将契约视为说明框架功能的简洁文档

除此之外,基于简单接口,代码更容易理解和维护。在一个庞大而复杂的类中,与其追踪哪些方法是有效的,倒不如直接查找一个简单、干净的接口来参考更为妥当。

如何使用契约

那么如何使用契约呢?其实很简单。

Laravel 中有很多类都是通过 服务容器,进行解析,包括控制器,以及事件监听器、中间件、队列任务,甚至路由闭包。所以,实现一个契约,你只需要在被解析的类的构造函数中添加契约接口即可。

例如,下面这个事件监听器:

  1. <?php
  2. namespace App\Listeners;
  3. use App\User;
  4. use App\Events\OrderWasPlaced;
  5. use Illuminate\Contracts\Redis\Database;
  6. class CacheOrderInformation
  7. {
  8. /**
  9. * Redis 数据库实现
  10. */
  11. protected $redis;
  12. /**
  13. * 创建事件处理器实例
  14. *
  15. * @param Database $redis
  16. * @return void
  17. */
  18. public function __construct(Database $redis)
  19. {
  20. $this->redis = $redis;
  21. }
  22. /**
  23. * 处理事件
  24. *
  25. * @param OrderWasPlaced $event
  26. * @return void
  27. */
  28. public function handle(OrderWasPlaced $event)
  29. {
  30. //
  31. }
  32. }

当事件监听器被解析的时候,服务容器会读取构造函数中的类型提示,并注入相应的值。想要学习更多关于服务容器的注册细节,请参考 其文档.

契约参考

下表提供了所有 Laravel 契约以及其对应的 Facades :

契约对应的 Facade
Illuminate\Contracts\Auth\Access\Authorizable
Illuminate\Contracts\Auth\Access\GateGate
Illuminate\Contracts\Auth\Authenticatable
Illuminate\Contracts\Auth\CanResetPassword
Illuminate\Contracts\Auth\FactoryAuth
Illuminate\Contracts\Auth\GuardAuth::guard()
Illuminate\Contracts\Auth\PasswordBrokerPassword::broker()
Illuminate\Contracts\Auth\PasswordBrokerFactoryPassword
Illuminate\Contracts\Auth\StatefulGuard
Illuminate\Contracts\Auth\SupportsBasicAuth
Illuminate\Contracts\Auth\UserProvider
Illuminate\Contracts\Bus\DispatcherBus
Illuminate\Contracts\Bus\QueueingDispatcherBus::dispatchToQueue()
Illuminate\Contracts\Broadcasting\FactoryBroadcast
Illuminate\Contracts\Broadcasting\BroadcasterBroadcast::connection()
Illuminate\Contracts\Broadcasting\ShouldBroadcast
Illuminate\Contracts\Broadcasting\ShouldBroadcastNow
Illuminate\Contracts\Cache\FactoryCache
Illuminate\Contracts\Cache\Lock
Illuminate\Contracts\Cache\LockProvider
Illuminate\Contracts\Cache\RepositoryCache::driver()
Illuminate\Contracts\Cache\Store
Illuminate\Contracts\Config\RepositoryConfig
Illuminate\Contracts\Console\Application
Illuminate\Contracts\Console\KernelArtisan
Illuminate\Contracts\Container\ContainerApp
Illuminate\Contracts\Cookie\FactoryCookie
Illuminate\Contracts\Cookie\QueueingFactoryCookie::queue()
Illuminate\Contracts\Database\ModelIdentifier
Illuminate\Contracts\Debug\ExceptionHandler
Illuminate\Contracts\Encryption\EncrypterCrypt
Illuminate\Contracts\Events\DispatcherEvent
Illuminate\Contracts\Filesystem\CloudStorage::cloud()
Illuminate\Contracts\Filesystem\FactoryStorage
Illuminate\Contracts\Filesystem\FilesystemStorage::disk()
Illuminate\Contracts\Foundation\ApplicationApp
Illuminate\Contracts\Hashing\HasherHash
Illuminate\Contracts\Http\Kernel
Illuminate\Contracts\Mail\MailQueueMail::queue()
Illuminate\Contracts\Mail\Mailable
Illuminate\Contracts\Mail\MailerMail
Illuminate\Contracts\Notifications\DispatcherNotification
Illuminate\Contracts\Notifications\FactoryNotification
Illuminate\Contracts\Pagination\LengthAwarePaginator
Illuminate\Contracts\Pagination\Paginator
Illuminate\Contracts\Pipeline\Hub
Illuminate\Contracts\Pipeline\Pipeline
Illuminate\Contracts\Queue\EntityResolver
Illuminate\Contracts\Queue\FactoryQueue
Illuminate\Contracts\Queue\Job
Illuminate\Contracts\Queue\MonitorQueue
Illuminate\Contracts\Queue\QueueQueue::connection()
Illuminate\Contracts\Queue\QueueableCollection
Illuminate\Contracts\Queue\QueueableEntity
Illuminate\Contracts\Queue\ShouldQueue
Illuminate\Contracts\Redis\FactoryRedis
Illuminate\Contracts\Routing\BindingRegistrarRoute
Illuminate\Contracts\Routing\RegistrarRoute
Illuminate\Contracts\Routing\ResponseFactoryResponse
Illuminate\Contracts\Routing\UrlGeneratorURL
Illuminate\Contracts\Routing\UrlRoutable
Illuminate\Contracts\Session\SessionSession::driver()
Illuminate\Contracts\Support\Arrayable
Illuminate\Contracts\Support\Htmlable
Illuminate\Contracts\Support\Jsonable
Illuminate\Contracts\Support\MessageBag
Illuminate\Contracts\Support\MessageProvider
Illuminate\Contracts\Support\Renderable
Illuminate\Contracts\Support\Responsable
Illuminate\Contracts\Translation\Loader
Illuminate\Contracts\Translation\TranslatorLang
Illuminate\Contracts\Validation\FactoryValidator
Illuminate\Contracts\Validation\ImplicitRule
Illuminate\Contracts\Validation\Rule
Illuminate\Contracts\Validation\ValidatesWhenResolved
Illuminate\Contracts\Validation\ValidatorValidator::make()
Illuminate\Contracts\View\Engine
Illuminate\Contracts\View\FactoryView
Illuminate\Contracts\View\ViewView::make()

本文章首发在 LearnKu.com 网站上。

本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接 我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。