Decorator Pattern 装饰者模式
纲要:
1. 一个初学者的疑惑
2. 装饰者模式的特点
3. 简单case掌握装饰者模式
4. laravel中装饰者模式的应用
Confusing:
刚开始研究laravel源码之前,对于"装饰者模式"我也是知之甚少,而对于“装饰者模式”的学习起因于创建一个中间件的时候,我始终都不太明白,中间件中的$next闭包是怎么传进来,因此好奇心强的我google了大量的前辈的博客和文章,学到了很多以前不知道的知识点,才明白中间件加载的原理,使我受益匪浅,在此非常感谢这些前辈的无私奉献.ok,回到正题.
装饰者模式的特点
详细介绍点我
“(1) 装饰对象和真实对象有相同的接口。这样客户端对象就能以和真实对象相同的方式和装饰对象交互。
(2) 装饰对象包含一个真实对象的引用(reference)
(3) 装饰对象接受所有来自客户端的请求。它把这些请求转发给真实的对象。
(4) 装饰对象可以在转发这些请求以前或以后增加一些附加功能。这样就确保了在运行时,不用修改给定对象的结构就可以在外部增加附加的功能。在面向对象的设计中,通常是通过继承来实现对给定类的功能扩展。” --------------引自<百度百科>
简单case掌握装饰者模式
注意:若是以前没接触过该模式,建议先看下这篇文章《laravel 框架关键技术解析》学习笔记之装饰者模式,再回头看本文效果更佳.
好吧,如果是第一次看装饰者模式,看上面特性"官方阐述",确实有点不知所云,ok,为方便大家能快速理解该模式,我写了一个"laravel源码简化版"的case,放代码之前给大家说下实现该模式最核心的东西,就靠两个方法,如下:
代码:
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;
}