思考并回答以下问题:
前言
当所有的路由都加载完毕后,就会根据请求的url来将请求分发到对应的路由上去。然而,在分发到路由之前还要经过各种中间件的计算。
Laravel利用装饰者模式来实现中间件的功能。
从原始装饰者模式到闭包装饰者
装饰者模式是设计模式的一种,主要进行对象的多次处理与过滤,是在开放-关闭原则下实现动态添加或减少功能的一种方式。下面先看一个装饰者模式的例子:
总共有两种咖啡:Decaf、Espresso,另有两种调味品:Mocha、Whip(3种设计的主要差别在于抽象方式不同)
装饰模式分为3个部分:
1,抽象组件 — 对应Coffee类
2,具体组件 — 对应具体的咖啡,如:Decaf,Espresso
3,装饰者 — 对应调味品,如:Mocha,Whip
原始装饰者模式
1 | public interface Coffee |
当我们使用装饰者模式的时候:
1 | public class Test |
我们可以看出来,装饰者模式就是利用装饰者类来对具体类不断的进行多层次的处理,首先我们创建了Espresso类,然后第一次利用Mocha装饰者对Espresso咖啡加了摩卡,第二次重复加了摩卡,第三次利用装饰者Whip对Espresso咖啡加了奶油。每次加入新的调料,装饰者都会对价格cost做一些处理(+0.1、+0.5)。
无构造函数的装饰者
我们对这个装饰者进行一些改造:
1 | public class Espresso |
改造后,装饰者类通过函数cost来注入具体类caffee,而不是通过构造函数,这样做有助于自动化进行装饰处理。我们改造后发现,想要对具体类通过装饰类进行处理,需要不断的调用cost函数,如果有10个装饰操作,就要手动写10个语句,因此我们继续进行改造:
闭包装饰者模式
1 | public class Espresso |
在这次改造中,我们使用了闭包函数,这样做的目的在于,我们只需要最后一句$fun3($coffee) ,就可以启动整个装饰链条。
闭包装饰者的抽象化
然而这种改造还不够深入,因为我们还可以把$fuc1、$fuc2、$fuc3继续抽象化为一个闭包函数,这个闭包函数仅仅是参数$fuc、$dressing每次不同,$coffee相同,因此改造如下:
1 | public class Test |
这次,我们把之前的闭包分为两个部分,$fun负责具体类的参数传递,$fuc负责装饰者和闭包函数的参数传递。在最后一句$fun3,只需要传递一个具体类,就可以启动整个装饰链条。
闭包装饰者的自动化
到这里,我们还有一件事没有完成,那就是$fuc1、$fuc2、$fuc3这些闭包的构建还是手动的,我们需要将这个过程改为自动的:
1 | public class Test |
Laravel的闭包装饰者——Pipeline
上一章我们说到了路由的注册启动与加载过程,这个过程由bootstrap()完成。当所有的路由加载完毕后,就要进行各种中间件的处理了:
1 | protected function sendRequestThroughRouter($request) |
Laravel的中间件处理由Pipeline来完成,它是一个闭包装饰者模式,其中request是具体类,相当于我们上面的caffee类;middleware中间件是装饰者类,相当于上面的dressing类;我们先看看这个类内部的代码:
1 | class Pipeline implements PipelineContract |
pipeline 的构造和我们上面所讲的闭包装饰者相同,我们着重来看carry()函数的代码:
1 | function ($stack, $pipe) { |
最外层的闭包相当于上个章节的 $fuc ,
1 | function ($passable) use ($stack, $pipe) { |
里面的这一层比闭包型党与上个章节的$fun,prepareDestination这个函数相当于上面的$fuc0 ,
1 | if ($pipe instanceof Closure) |
这一部分相当于上个章节的$dressing->cost($coffee, $fuc);这部分主要解析中间件handle()函数的参数:
1 | public function via($method) |
这样,Laravel就实现了中间件对request的层层处理。