说明:Laravel中Middleware的实现主要利用了Decorator Pattern的设计,本文主要先学习下Decorator Pattern如何实现,为后面学习Middleware的设计做个铺垫。Decorator Pattern和Adapter Pattern会有很多相似之处,但相比较于Adapter Pattern重点突出adapter,Decorator Pattern重点突出的是wrapper,两个不是同一概念。

开发环境:Laravel5.3 + PHP7 + OS X 10.11

Decorator Pattern

Decorator Pattern作为一种结构型模式,可以给现有对象Component装饰decorate几个feature,而不影响原有的Component对象,这几个feature就是装饰对象Decorator。这种设计很好用,因为可以随时增加或减少想要的feature,并且增加或减少这种操作又很简单,实现了程序松耦合。就像Laravel中每一个middleware就是一个feature,如果想要增加一个不缓存request的feature,可以增加一个middleware假设叫做NoCacheMiddleware,写好后只需要在app/Http/Kernel.php文件中添加下配置就可。看下一个简单的demo实例,看看如何使用Decorator Pattern。
先定义一个IMiddleware的接口,保证设计的features都是同一物种,即只有实现了该接口的feature才称为middleware:

namespace MyRightCapital\Development\DecoratorPattern;interface IMiddleware
{public function handle();
}

在该接口中定义一个handle()函数,每一个feature必须实现这个handle()来做逻辑。现在需要设计5个features,并且每一个feature都必须是middleware:

$features = [CheckForMaintenanceMode::class,AddQueuedCookiesToResponse::class,StartSession::class,ShareErrorsFromSession::class,VerifyCsrfToken::class,
];

OK,现在实现第一个feature,并改造为middleware:

namespace MyRightCapital\Development\DecoratorPattern;class CheckForMaintenanceMode implements IMiddleware
{/*** @var \MyRightCapital\Development\DecoratorPattern\IMiddleware*/private $middleware;/*** CheckForMaintenanceMode constructor.** @param \MyRightCapital\Development\DecoratorPattern\IMiddleware $middleware*/public function __construct(IMiddleware $middleware){$this->middleware = $middleware;}public function handle(){echo 'Check if the application is in the maintenance status.' . PHP_EOL;$this->middleware->handle();}
}

第一个middleware是CheckForMaintenanceMode,需要检查程序是否处于维护模式。实现第二个feature,并改造为middleware:

namespace MyRightCapital\Development\DecoratorPattern;class AddQueuedCookiesToResponse implements IMiddleware
{/*** @var \MyRightCapital\Development\DecoratorPattern\IMiddleware*/private $middleware;/*** AddQueuedCookiesToResponse constructor.** @param \MyRightCapital\Development\DecoratorPattern\IMiddleware $middleware*/public function __construct(IMiddleware $middleware){$this->middleware = $middleware;}public function handle(){$this->middleware->handle();echo 'Add queued cookies to the response' . PHP_EOL;}
}

第二个middleware实现把cookie添加到response。实现第三个feature,并改造为middleware:

namespace MyRightCapital\Development\DecoratorPattern;class StartSession implements IMiddleware
{/*** @var \MyRightCapital\Development\DecoratorPattern\IMiddleware*/private $middleware;/*** StartSession constructor.** @param \MyRightCapital\Development\DecoratorPattern\IMiddleware $middleware*/public function __construct(IMiddleware $middleware){$this->middleware = $middleware;}public function handle(){echo 'Start session of this request.' . PHP_EOL;$this->middleware->handle();echo 'Close session of this request.' . PHP_EOL;}
}

第三个feature主要实现开启和关闭session。实现第四个feature,并改造为middleware:

class ShareErrorsFromSession implements IMiddleware
{/*** @var \MyRightCapital\Development\DecoratorPattern\IMiddleware*/private $middleware;/*** ShareErrorsFromSession constructor.** @param \MyRightCapital\Development\DecoratorPattern\IMiddleware $middleware*/public function __construct(IMiddleware $middleware){$this->middleware = $middleware;}public function handle(){$this->middleware->handle();echo 'Share the errors variable from request to the views.' . PHP_EOL;}
}

第四个feature主要实现共享变量$errors,以便在视图中使用该变量。实现第五个feature,并改造为middleware:

namespace MyRightCapital\Development\DecoratorPattern;class VerifyCsrfToken implements IMiddleware
{/*** @var \MyRightCapital\Development\DecoratorPattern\IMiddleware*/private $middleware;/*** VerifyCsrfToken constructor.** @param \MyRightCapital\Development\DecoratorPattern\IMiddleware $middleware*/public function __construct(IMiddleware $middleware){$this->middleware = $middleware;}public function handle(){echo 'Verify csrf token when post request.' . PHP_EOL;$this->middleware->handle();}
}

第五个feature主要实现CSRF验证。OK,现在每一个feature都已经实现了,并将作为Decorator来装饰初始的Component。

OK,Decorator Pattern中已经有了五个Decorators,现在需要实现一个Component,然后用这五个Decorators来装饰Component。
现在定义一个Component接口,保证Component与Decorator是相似物种,并且Component又有自己的实现接口:

namespace MyRightCapital\Development\DecoratorPattern;interface IComponent extends IMiddleware
{public function getRequest();
}

现在构造一个Component:

namespace MyRightCapital\Development\DecoratorPattern;class Request implements IComponent
{public function handle(){echo 'This is a request from the client. And this request will go through the middlewares.' . PHP_EOL;}public function getRequest(){return $this;}
}

OK,在Decorator Pattern中,目前已经构造好了Component和Decorator。把Component和Decorator拼接在一起的场所是Client,所以需要造一个Client类,在其内部实现对Component的Decorate操作:

namespace MyRightCapital\Development\DecoratorPattern;class Client
{/*** @var \MyRightCapital\Development\DecoratorPattern\Request*/protected $request;/*** @var \MyRightCapital\Development\DecoratorPattern\IMiddleware*/protected $response;public function __construct(){// Component$this->request  = new Request();// Decorate the Component$this->response = $this->wrapDecorator($this->request);}/*** @param \MyRightCapital\Development\DecoratorPattern\IMiddleware $decorator** @return \MyRightCapital\Development\DecoratorPattern\IMiddleware*/public function wrapDecorator(IMiddleware $decorator){$decorator = new VerifyCsrfToken($decorator);$decorator = new ShareErrorsFromSession($decorator);$decorator = new StartSession($decorator);$decorator = new AddQueuedCookiesToResponse($decorator);$response  = new CheckForMaintenanceMode($decorator);return $response;}/*** @return \MyRightCapital\Development\DecoratorPattern\IMiddleware*/public function getResponse(){return $this->response->handle();}
}

Client中wrapDecorator()实现了把原有的Component进过5个Middlewares的装饰后得到的新的Component,新的Component还是IMiddleware的实现,还是原来的物种。整个UML图:

OK,现在执行整个Decorator Pattern,看看是不是这些middlewares已经被装饰进原来的Component,创建一个index.php文件:

// 加载composer的autoload.php文件
include __DIR__ . '/../../../vendor/autoload.php';$client = new \MyRightCapital\Development\DecoratorPattern\Client();
$client->getResponse();

php index.php文件看看输出什么:

Check if the application is in the maintenance status.
Start session of this request.
Verify csrf token when post request.
This is a request from the client. And this request will go through the middlewares.
Share the errors variable from request to the views.
Close session of this request.
Add queued cookies to the response.

的确,五个middlewares已经装饰了原有的component,并检查下装饰次序是否是正确的?
实际上,Client中的$this->response等同于:

$response = new CheckForMaintenanceMode(new AddQueuedCookiesToResponse(new StartSession(new ShareErrorsFromSession(new VerifyCsrfToken(new Request())))));

所以,执行次序是:

1. CheckForMaintenanceMode::handle() -> 先执行 echo 'Check if the application is in the maintenance status.', 然后执行 AddQueuedCookiesToResponse::handle()
2. AddQueuedCookiesToResponse::handle() -> 先执行 StartSession::handle(), 然后执行 echo 'Add queued cookies to the response.'
3. StartSession::handle() -> 先执行 echo 'Start session of this request.', 然后执行 ShareErrorsFromSession::handle(), 最后执行 echo 'Close session of this request.'
4. ShareErrorsFromSession::handle() -> 先执行VerifyCsrfToken::handle(), 然后执行 echo 'Share the errors variable from request to the views.'
5. VerifyCsrfToken::handle() -> 先执行 echo 'Verify csrf token when post request.', 然后执行 Request::handle()
6. Request::handle() -> 执行 echo 'This is a request from the client. And this request will go through the middlewares.'// So,执行顺序等同于:
echo 'Check if the application is in the maintenance status.' ->
echo 'Start session of this request.' ->
echo 'Verify csrf token when post request.' ->
echo 'This is a request from the client. And this request will go through the middlewares.' ->
echo 'Share the errors variable from request to the views.' ->
echo 'Close session of this request.' ->
echo 'Add queued cookies to the response.' ->

在Laravel里每一个Middleware中有前置操作和后置操作。在本demo里echo语句前置于$this->middleware->handle();则为前置操作,后置则为后置操作。
OK,再加一个Kernel类,保证Request经过Middleware的前置操作后进入Kernel,然后从Kernel出来进入Middlewares的后置操作,一步步过滤:

namespace MyRightCapital\Development\DecoratorPattern;interface IKernel extends IMiddleware
{}class Kernel implements IKernel
{public function handle(){echo 'Kernel handle the request, and send the response.' . PHP_EOL;}
}// 修改Request
class Request implements IRequest
{/*** @var \MyRightCapital\Development\DecoratorPattern\IKernel*/private $kernel;public function __construct(IKernel $kernel){$this->kernel = $kernel;}public function handle(){echo 'This request has been filtering by the before action in the middlewares, and go into the kernel.' . PHP_EOL;$this->kernel->handle();echo 'The request has been handled by the kernel, and will be send to the after action in the middlewares' . PHP_EOL;}public function getRequest(){return $this;}
}// 修改下Client的构造函数
public function __construct(){// Component$this->request = new Request(new Kernel());// Decorate the Component$this->response = $this->wrapDecorator($this->request);}

则再次执行index.php文件,得到:

Check if the application is in the maintenance status.
Start session of this request.
Verify csrf token when post request.
This request has been filtering by the before action in the middlewares, and go into the kernel.
Kernel handle the request, and send the response.
The request has been handled by the kernel, and will be send to the after action in the middlewares
Share the errors variable from request to the views.
Close session of this request.
Add queued cookies to the response.

具体流程上文已经讨论,可画一张草图展示处理流程,其中Before表示该Middleware的前置操作,After表示该Middleware的后置操作:

OK,使用Decorator Pattern来层层过滤Request,并实现分层,最后进入Kernel执行得到Response,然后Response经过层层过滤,返回给客户端。非常赞的设计。

总结:本文主要学习Laravel如何使用Decorator Pattern来设计Middleware。下一篇学习下Laravel中Middleware的源码。

欢迎关注Laravel-China。

RightCapital招聘Laravel DevOps

Laravel学习笔记之Decorator Pattern相关推荐

  1. 设计模式学习笔记——装饰(Decorator)模式

    设计模式学习笔记--装饰(Decorator)模式 @(设计模式)[设计模式, 装饰模式, decorator] 设计模式学习笔记装饰Decorator模式 基本介绍 装饰案例 类图 实现代码 Dis ...

  2. Laravel学习笔记汇总——Collection方法详解

    ## Laravel学习笔记汇总--Collection方法详解 本文参考:https:// laravel.com/docs/8.x/collections // 返回整个底层的数组 collect ...

  3. Laravel学习笔记目录

    伴随自己学习.使用laravel的过程,记录下遇到过的问题与思考.与程序猿们共勉,知识的道路没有止境,我们一直都在学习的路上. 第一部分:入门级 这部分文章写得都是比较浅显的,属于纯粹的使用,我也不知 ...

  4. Laravel学习笔记(二)

    解决了类自动加载的问题,剩下的问题就是看文档了,laravel的官方文档虽然简单,但是却包含了很多基础知识,学习Laravel最好先看看官方文档,我感觉帮助很大,因为laravel框架的源码看起来并不 ...

  5. Laravel 学习笔记之 Query Builder 源码解析(下)

    说明:本文主要学习下Query Builder编译Fluent Api为SQL的细节和执行SQL的过程.实际上,上一篇聊到了\Illuminate\Database\Query\Builder这个非常 ...

  6. 三、PHP框架Laravel学习笔记——路由参数、重定向、视图

    一.路由参数 我们已经学习了部分路由参数的功能,比如动态传递{id}: 那么,有时这个参数需要进行约束,我们可以使用正则来限定必须是数字: Route::get('task/read/{id}', ' ...

  7. Laravel学习笔记之冒泡、快速、选择和插入排序(持续更新)

    说明:本文是对个人学习冒泡.快速.选择和插入排序的小总结.面试经常问这些东西,虽然不知道为啥老爱问这些,该问的又不问.不管咋样,个人学习MySQL时有关索引就用到快速排序,索引也是以B+Tree数据结 ...

  8. Laravel 学习笔记 —— 神奇的服务容器

    原文地址: http://www.insp.top/learn-laravel-container ,转载务必保留来源,谢谢了! 容器,字面上理解就是装东西的东西.常见的变量.对象属性等都可以算是容器 ...

  9. Laravel学习笔记(四)数据库 数据库迁移案例

    创建迁移 首先,让我们创建一个MySql数据库"Laravel_db".接下来打开app/config目录下的database.php文件.请确保default键值是mysql: ...

最新文章

  1. Appium进行脚本录制操作模拟器
  2. Django之中间件-CSRF
  3. java 20_java20 - 芥末小弟 - OSCHINA - 中文开源技术交流社区
  4. hive cli启动判断hadoop version的问题
  5. ARMV8 datasheet学习笔记5:异常模型
  6. [forwarding]Android上dip、dp、px、sp等单位说明
  7. 超市扫码机器服务器系统搭建,超市用的扫码机是怎么个原理?
  8. html调用wmp,web页面中嵌入window media player,支持IE和Chrome
  9. 基于R语言对哺乳动物睡眠时间sleep数据集的分析
  10. CSS——微信朋友圈图片样式实现方法
  11. 云计算技术的概念、原理
  12. 鸿蒙系统敏感应用,鸿蒙系统特性“揭晓”!一次开发灵活使用,生态构建难题被解决?...
  13. 一些程序员的找工作常见的面试问题
  14. 906.nethogs安装使用
  15. 数据我爬定了,限流也挡不住,我说的
  16. QGis二次开发基础 -- 构建图层管理器
  17. 初学CAD,该如何正确查看CAD图纸呢?
  18. CSS在浏览器不能加载出来的解决方案
  19. HackMyvm(五)Warrior持续更新
  20. “区块链”究竟是什么鬼

热门文章

  1. LeetCode 98. Validate Binary Search Tree--C++解法--判断是否是BST--递归,迭代做法,中序遍历
  2. Reids报错解决:Job for redis-server.service failed because the control process exited with error code.
  3. redis 主从复制的核心原理
  4. Spring Boot Bean的使用,@Repository,@Service,@Controller,@Component
  5. C++_泛型编程与标准库(五)
  6. android打不开链接,安卓的webView的loadUrl打不开,太长的url超链接,求解
  7. expert one on one oracle,数据库表——EXPERT ONE-ON-ONE ORACLE
  8. json boolean android,如何解析Android中的JSON?
  9. k8s 通过环境变量获取Pod信息
  10. Spring 注解 @bean 和 @component 的区别, 你知道吗?