Decorator Pattern With Laravel 裝飾者模式

Decorator Pattern 裝飾者模式

綱要:php

1. 一個初學者的疑惑
 
 2. 裝飾者模式的特色
 
 3. 簡單case掌握裝飾者模式
 
 4. laravel中裝飾者模式的應用

Confusing:
剛開始研究laravel源碼以前,對於"裝飾者模式"我也是知之甚少,而對於「裝飾者模式」的學習原由於建立一箇中間件的時候,我始終都不太明白,中間件中的$next閉包是怎麼傳進來,所以好奇心強的我google了大量的前輩的博客和文章,學到了不少之前不知道的知識點,才明白中間件加載的原理,使我受益不淺,在此很是感謝這些前輩的無私奉獻.ok,回到正題.laravel

clipboard.png

裝飾者模式的特色
詳細介紹點我程序員

「(1) 裝飾對象和真實對象有相同的接口。這樣客戶端對象就能以和真實對象相同的方式和裝飾對象交互。
(2) 裝飾對象包含一個真實對象的引用(reference)
(3) 裝飾對象接受全部來自客戶端的請求。它把這些請求轉發給真實的對象。
(4) 裝飾對象能夠在轉發這些請求之前或之後增長一些附加功能。這樣就確保了在運行時,不用修改給定對象的結構就能夠在外部增長附加的功能。在面向對象的設計中,一般是經過繼承來實現對給定類的功能擴展。」 --------------引自<百度百科>bootstrap

簡單case掌握裝飾者模式
注意:如果之前沒接觸過該模式,建議先看下這篇文章《laravel 框架關鍵技術解析》學習筆記之裝飾者模式,再回頭看本文效果更佳.
好吧,若是是第一次看裝飾者模式,看上面特性"官方闡述",確實有點不知所云,ok,爲方便你們能快速理解該模式,我寫了一個"laravel源碼簡化版"的case,放代碼以前給你們說下實現該模式最核心的東西,就靠兩個方法,以下:segmentfault

  1. call_user_func() 不懂就點我閉包

  2. array_reduce() 你應該看看我app

代碼:框架

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;
        }
相關文章
相關標籤/搜索