綱要:php
1. 一個初學者的疑惑 2. 裝飾者模式的特色 3. 簡單case掌握裝飾者模式 4. laravel中裝飾者模式的應用
Confusing:
剛開始研究laravel源碼以前,對於"裝飾者模式"我也是知之甚少,而對於「裝飾者模式」的學習原由於建立一箇中間件的時候,我始終都不太明白,中間件中的$next閉包是怎麼傳進來,所以好奇心強的我google了大量的前輩的博客和文章,學到了不少之前不知道的知識點,才明白中間件加載的原理,使我受益不淺,在此很是感謝這些前輩的無私奉獻.ok,回到正題.laravel
裝飾者模式的特色
詳細介紹點我程序員
「(1) 裝飾對象和真實對象有相同的接口。這樣客戶端對象就能以和真實對象相同的方式和裝飾對象交互。
(2) 裝飾對象包含一個真實對象的引用(reference)
(3) 裝飾對象接受全部來自客戶端的請求。它把這些請求轉發給真實的對象。
(4) 裝飾對象能夠在轉發這些請求之前或之後增長一些附加功能。這樣就確保了在運行時,不用修改給定對象的結構就能夠在外部增長附加的功能。在面向對象的設計中,一般是經過繼承來實現對給定類的功能擴展。」 --------------引自<百度百科>bootstrap
簡單case掌握裝飾者模式
注意:如果之前沒接觸過該模式,建議先看下這篇文章《laravel 框架關鍵技術解析》學習筆記之裝飾者模式,再回頭看本文效果更佳.
好吧,若是是第一次看裝飾者模式,看上面特性"官方闡述",確實有點不知所云,ok,爲方便你們能快速理解該模式,我寫了一個"laravel源碼簡化版"的case,放代碼以前給你們說下實現該模式最核心的東西,就靠兩個方法,以下:segmentfault
代碼:框架
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()函數調用返回的是一個閉包:ide
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; }