解析Laravel框架下的Contracts契约
Contracts
Laravel 的契约是一组定义框架提供的核心服务的接口, 例如我们在介绍用户认证的章节中到的用户看守器契约IllumninateContractsAuthGuard
和用户提供器契约IlluminateContractsAuthUserProvider
以及框架自带的App\User模型所实现的IlluminateContractsAuthAuthenticatable
契约。
为什么使用契约
通过上面几个契约的源码文件我们可以看到,Laravel提供的契约是为核心模块定义的一组interface。Laravel为每个契约都提供了相应的实现类,下表列出了Laravel为上面提到的三个契约提供的实现类。
所以在自己开发的项目中,如果Laravel提供的用户认证系统无法满足需求,你可以根据需求定义看守器和用户提供器的实现类,比如我之前做的项目就是用户认证依赖于公司的员工管理系统的API,所以我就自己写了看守器和用户提供器契约的实现类,让Laravel通过自定义的Guard和UserProvider来完成用户认证。自定义用户认证的方法在介绍用户认证的章节中我们介绍过,读者可以去翻阅那块的文章。
所以Laravel为所有的核心功能都定义契约接口的目的就是为了让开发者能够根据自己项目的需要自己定义实现类,而对于这些接口的消费者(比如:Controller、或者内核提供的 AuthManager这些)他们不需要关心接口提供的方法具体是怎么实现的, 只关心接口的方法能提供什么功能然后去使用这些功能就可以了,我们可以根据需求在必要的时候为接口更换实现类,而消费端不用进行任何改动。
定义和使用契约
上面我们提到的都是Laravel内核提供的契约, 在开发大型项目的时候我们也可以自己在项目中定义契约和实现类,你有可能会觉得自带的Controller、Model两层就已经足够你编写代码了,凭空多出来契约和实现类会让开发变得繁琐。我们先从一个简单的例子出发,考虑下面的代码有什么问题:
class OrderController extends Controller { public function getUserOrders() { $orders= Order::where('user_id', '=', \Auth::user()->id)->get(); return View::make('order.index', compact('orders')); } }
这段代码很简单,但我们要想测试这段代码的话就一定会和实际的数据库发生联系。
也就是说, ORM和这个控制器有着紧耦合。如果不使用Eloquent ORM,不连接到实际数据库,我们就没办法运行或者测试这段代码。这段代码同时也违背了“关注分离”这个软件设计原则。
简单讲:这个控制器知道的太多了。
控制器不需要去了解数据是从哪儿来的,只要知道如何访问就行。控制器也不需要知道这数据是从MySQL或哪儿来的,只需要知道这数据目前是可用的。
Separation Of Concerns 关注分离
Every class should have a single responsibility, and that responsibility should be entirely encapsulated by the class.
每个类都应该只有单一的职责,并且职责里所有的东西都应该由这个类封装
接下来我们定义一个接口,然后实现该接口
interface OrderRepositoryInterface { public function userOrders(User $user); } class OrderRepository implements OrderRepositoryInterface { public function userOrders(User $user) { Order::where('user_id', '=', $user->id)->get(); } }
将接口的实现绑定到Laravel的服务容器中
App::singleton('OrderRepositoryInterface', 'OrderRespository');
然后我们将该接口的实现注入我们的控制器
class UserController extends Controller { public function __construct(OrderRepositoryInterface $orderRepository) { $this->orders = $orderRespository; } public function getUserOrders() { $orders = $this->orders->userOrders(); return View::make('order.index', compact('orders')); } }
现在我们的控制器就完全和数据层面无关了。在这里我们的数据可能来自MySQL,MongoDB或者Redis。我们的控制器不知道也不需要知道他们的区别。这样我们就可以独立于数据层来测试Web层了,将来切换存储实现也会很容易。
接口与团队开发
当你的团队在开发大型应用时,不同的部分有着不同的开发速度。
比如一个开发人员在开发数据层,另一个开发人员在做控制器层。
写控制器的开发者想测试他的控制器,不过数据层开发较慢没法同步测试。那如果两个开发者能先以interface的方式达成协议,后台开发的各种类都遵循这种协议。
一旦建立了约定,就算约定还没实现,开发者也可以为这接口写个“假”实现
class DummyOrderRepository implements OrderRepositoryInterface { public function userOrders(User $user) { return collect(['Order 1', 'Order 2', 'Order 3']); } }
一旦假实现写好了,就可以被绑定到IoC容器里
App::singleton('OrderRepositoryInterface', 'DummyOrderRepository');
然后这个应用的视图就可以用假数据填充了。接下来一旦后台开发者写完了真正的实现代码,比如叫RedisOrderRepository
。
那么使用IoC容器切换接口实现,应用就可以轻易地切换到真正的实现上,整个应用就会使用从Redis读出来的数据了。
接口与测试
建立好接口约定后也更有利于我们在测试时进行Mock
public function testIndexActionBindsUsersFromRepository() { // Arrange... $repository = Mockery::mock('OrderRepositoryInterface'); $repository->shouldReceive('userOrders')->once()->andReturn(['order1', 'order2]); App::instance('OrderRepositoryInterface', $repository); // Act... $response = $this->action('GET', 'OrderController@getUserOrders'); // Assert... $this->assertResponseOk(); $this->assertViewHas('order', ['order1', 'order2']); }
总结
接口在程序设计阶段非常有用,在设计阶段与团队讨论完成功能需要制定哪些接口,然后设计出每个接口具体要实现的方法,方法的入参和返回值这些,每个人就可以按照接口的约定来开发自己的模块,遇到还没实现的接口完全可以先定义接口的假实现等到真正的实现开发完成后再进行切换,这样既降低了软件程序结构中上层对下层的耦合也能保证各部分的开发进度不会过度依赖其他部分的完成情况。
更多学习内容请访问:
怎么从一名码农成为架构师的必看知识点:目录大全(不定期更新)zhuanlan.zhihu.com
解析Laravel框架下的Contracts契约的更多相关文章
- Laravel框架下路由的使用(源码解析)
本篇文章给大家带来的内容是关于Laravel框架下路由的使用(源码解析),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助. 前言 我的解析文章并非深层次多领域的解析攻略.但是参考着开发文 ...
- Laravel框架下容器Container 的依赖注入和反射应用
依赖注入,简单说是把类里头依赖的对象,置于类外头,即客户端调用处.相当于把类与类解耦. 一个简单的例子: class A { public function __construct() { // 这种 ...
- 基于Laravel框架下使用守护进程supervisor实现定时任务(毫秒)
本篇文章给大家带来的内容是关于基于Laravel框架下使用守护进程supervisor实现定时任务(毫秒),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助. 公司需要实现X分钟内每隔Y秒 ...
- 装饰器模式以及Laravel框架下的中间件应用
Laravel框架的中间件使用:从请求进来到响应返回,经过中间件的层层包装,这种场景很适合用到一种设计模式---装饰器模式. 装饰器模式的作用,多种外界因素改变对象的行为.使用继承的方式改变行为不太被 ...
- EasyWeChat使用(laravel框架下)
最近做了个项目是关于微信网页开发的,今天记录下在做项目中的关于微信这块遇到的一些坑 关于微信这块,用的是EasyWeChat,提高了开发的效率.在看EasyWeChat这个文档的时候发现了有专门针对l ...
- 全面解析Pytorch框架下模型存储,加载以及冻结
最近在做试验中遇到了一些深度网络模型加载以及存储的问题,因此整理了一份比较全面的在 PyTorch 框架下有关模型的问题.首先咱们先定义一个网络来进行后续的分析: 1.本文通用的网络模型 import ...
- laravel 框架 下拉分页
<!doctype html><html lang="en"><head> <meta charset="UTF-8" ...
- laravel框架中所用到的依赖注入
用Laravel开发前前后后有2个月左右了,之前一直写Java,就像找到Java和PHP之前的共同点,用Java的某些原理去理解PHP会发现还是有很多共通之处的.Java的依赖注入已经是一个很常见的概 ...
- Laravel框架使用融云服务端SDK
方法一: 使用第三方依赖安装(在项目根目录) 第一步:安装 composer require latrell/rongcloud dev-master 第二步:更新依赖包 c ...
随机推荐
- 深入JVM内核--常用JVM配置参数
Trace跟踪参数 -verbose:gc -XX:+printGC 可以打印GC的简要信息 [GC 4790K->374K(15872K), 0.0001606 secs] [GC 4790K ...
- [LC] 505. The Maze II
There is a ball in a maze with empty spaces and walls. The ball can go through empty spaces by rolli ...
- java学习——反射机制
/* * JAVA反射机制是在运行状态中,对于任意一个类 (class文件),都能够知道这个类的所有属性和方法: * 对于任意一个对象,都能够调用它的任意一个方法和属性: * 这种动态获取的信息以及动 ...
- 仿射密码Python实现
算法分析 仿射密码结合了移位密码和乘数密码的特点,是移位密码和乘数密码的组合. 仿射密码的加密算法就是一个线性变化,即对明文字符x,对应的密文字符为y=ax+b(mod26)其中,a, b属于Z26且 ...
- AQS总结
前言 AQS(Abstract Queued Synchronizer)是JUC并发包中的核心基础组件,作者是大名鼎鼎的Doug Lea.通过AQS可以实现大部分的同步需求. 宏观架构 AQS包括一个 ...
- 杂记:Linux下gcc升级
公司要求,需要在CentOS6.5系统下进行一些测试.因为编写的测试程序中使用了一些C++11之后新增的特性,而CentOS6.5中安装的gcc版本为4.4.7,并不支持C++11,所以需要对gcc进 ...
- word2vec的简单理解
word2vec研究如何将词用向量表示,使用的两个重要模型--CBOW模型(Continuous Bag-of-Words Model)和Skip-gram模型(Continuous Skip-gra ...
- Spring源码阅读笔记03:xml配置读取
前面的文章介绍了IOC的概念,Spring提供的bean容器即是对这一思想的具体实现,在接下来的几篇文章会侧重于探究这一bean容器是如何实现的.在此之前,先用一段话概括一下bean容器的基本工作原理 ...
- Web 项目刚要打包,却找不到项目资源?
编程无小事,不管是语言层面还是工具层面,都要熟悉,方能在编程中过程中众享丝滑,不然就随处卡顿,耗费时间不说,还没有任何成就感.撸码过程中用 Idea 也很多年了,工具或环境遇到问题,问下度娘就完事了, ...
- Class file version does not support constant tag 16 in class file
启动服务时提示 Caused by: java.lang.ClassFormatError: Class file version does not support constant tag 16 i ...