Decorator Pattern 装饰者模式
纲要:
1. 一个初学者的疑惑 2. 装饰者模式的特点 3. 简单case掌握装饰者模式 4. laravel中装饰者模式的应用
Confusing:
刚开始研究laravel源码之前,对于"装饰者模式"我也是知之甚少,而对于“装饰者模式”的学习起因于创建一个中间件的时候,我始终都不太明白,中间件中的$next闭包是怎么传进来,因此好奇心强的我google了大量的前辈的博客和文章,学到了很多以前不知道的知识点,才明白中间件加载的原理,使我受益匪浅,在此非常感谢这些前辈的无私奉献.ok,回到正题.
装饰者模式的特点
“(1) 装饰对象和真实对象有相同的接口。这样客户端对象就能以和真实对象相同的方式和装饰对象交互。
(2) 装饰对象包含一个真实对象的引用(reference)(3) 装饰对象接受所有来自客户端的请求。它把这些请求转发给真实的对象。(4) 装饰对象可以在转发这些请求以前或以后增加一些附加功能。这样就确保了在运行时,不用修改给定对象的结构就可以在外部增加附加的功能。在面向对象的设计中,通常是通过继承来实现对给定类的功能扩展。” --------------引自<百度百科>简单case掌握装饰者模式
注意:若是以前没接触过该模式,建议先看下这篇文章,再回头看本文效果更佳.好吧,如果是第一次看装饰者模式,看上面特性"官方阐述",确实有点不知所云,ok,为方便大家能快速理解该模式,我写了一个"laravel源码简化版"的case,放代码之前给大家说下实现该模式最核心的东西,就靠两个方法,如下:call_user_func()
array_reduce()
代码:
interface func{ public static function handle($next); } class beauty implements func{ public static function say($next){ $next(); echo "我看不上屌丝"; } } class guy implements func{ public static function say($next){ echo '从前有个程序员想找个女朋友.'; $next(); } } $e=[guy::class,beauty::class]; function getClosure(){ return function ($a,$b){ return function()use($a,$b){ $b::say($a); }; }; } $d = function(){ echo '找了许久,仍未任何起色,突然有一天,碰见心仪的女神,结果,女神说:'."\n";}; call_user_func(array_reduce($e,getClosure(),$d));
先别着急看代码,先让代码运行下,看看结果是什么怎么样的?然后再去分析代码, 效果会好一点.ok,为了照顾新手朋友,我把执行过程,给大家梳理一下吧.
执行流程:
第一步.首先getClosure()函数调用返回的是一个闭包:function($a,$b){ return function()use($a,$b){ return $b::say($a); }}
第二步.
将$d和$e[0]作为第一步返回结果的参数并且执行,返回结果:function()use($a,$b){ #此时$b="guy" #$a = $d=function(){echo '找了许久,仍未任何起色,突然有一天,碰见心仪的女神,结果,女神说:'."\n";} return $b::say($a); }
第三步.
将第二部的结果和$e[1]作为第一步返回结果的参数并且执行,返回结果:function()use($a,$b){ #此时$b="beauty" $a='第二步骤返回结果' # $a = function()use($,'beacuty'){ # beauty::say(function()use('guy',$d){ # return guy::say($d); # }) # } return $b::say($a); }
第四步.执行call_user_func()调用array_reduce()返回的闭包即第三步的结果.
beauty::say($d) ==>$d()=>echo '从前有个程序员想找个女朋友.'; =>echo "'找了许久,仍未任何起色,突然有一天,碰见心仪的女神,结果,女神说:"=>"我看不上屌丝"执行过程类似如图(仔细体会,好似洋葱一样,从最外层渗透进去到最内层,再从最内层到最外层):
laravel中装饰者模式的应用
这里给出laravel框架的源码进行对比分析.文件index.php line 50 $kernel = $app->make(Illuminate\Contracts\Http\Kernel::class); $response = $kernel->handle( $request = Illuminate\Http\Request::capture());文件Illuminate\Foundation\Http\Kernel.php public function handle($request) { try { $request->enableHttpMethodParameterOverride(); $response = $this->sendRequestThroughRouter($request); } catch (Exception $e) { ....省略异常处理 } $this->app['events']->fire('kernel.handled', [$request, $response]); return $response; } protected function sendRequestThroughRouter($request) { $this->app->instance('request', $request); Facade::clearResolvedInstance('request'); $this->bootstrap(); return (new Pipeline($this->app)) ->send($request) ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware) ->then($this->dispatchToRouter()); } protected function dispatchToRouter() { return function ($request) { $this->app->instance('request', $request); return $this->router->dispatch($request); }; }文件Illuminate\PipeLine\PipeLine.php public function send($passable) { $this->passable = $passable; return $this; } public function through($pipes) { $this->pipes = is_array($pipes) ? $pipes : func_get_args(); return $this; } public function then(Closure $destination) { $firstSlice = $this->getInitialSlice($destination); $pipes = array_reverse($this->pipes); return call_user_func( array_reduce($pipes, $this->getSlice(), $firstSlice), $this->passable ); } protected function getInitialSlice(Closure $destination) { return function ($passable) use ($destination) { return call_user_func($destination, $passable); }; } protected function getSlice() { return function ($stack, $pipe) { return function ($passable) use ($stack, $pipe) { // If the pipe is an instance of a Closure, we will just call it directly but // otherwise we'll resolve the pipes out of the container and call it with // the appropriate method and arguments, returning the results back out. if ($pipe instanceof Closure) { return call_user_func($pipe, $passable, $stack); } else { list($name, $parameters) = $this->parsePipeString($pipe); return call_user_func_array([$this->container->make($name), $this->method], array_merge([$passable, $stack], $parameters)); } }; }; }文件Illuminate\Foundation\Application.php public function shouldSkipMiddleware() { return $this->bound('middleware.disable') && $this->make('middleware.disable') === true; }