Laravel源码解析

  1. 首页
  2. 文档
  3. Laravel源码解析
  4. Decorator Pattern With Laravel 装饰者模式

Decorator Pattern With Laravel 装饰者模式

Decorator Pattern 装饰者模式

纲要:

 1. 一个初学者的疑惑
 
 2. 装饰者模式的特点
 
 3. 简单case掌握装饰者模式
 
 4. laravel中装饰者模式的应用

Confusing:
刚开始研究laravel源码之前,对于”装饰者模式”我也是知之甚少,而对于“装饰者模式”的学习起因于创建一个中间件的时候,我始终都不太明白,中间件中的$next闭包是怎么传进来,因此好奇心强的我google了大量的前辈的博客和文章,学到了很多以前不知道的知识点,才明白中间件加载的原理,使我受益匪浅,在此非常感谢这些前辈的无私奉献.ok,回到正题.

clipboard.png

装饰者模式的特点
详细介绍点我

“(1) 装饰对象和真实对象有相同的接口。这样客户端对象就能以和真实对象相同的方式和装饰对象交互。
(2) 装饰对象包含一个真实对象的引用(reference)
(3) 装饰对象接受所有来自客户端的请求。它把这些请求转发给真实的对象。
(4) 装饰对象可以在转发这些请求以前或以后增加一些附加功能。这样就确保了在运行时,不用修改给定对象的结构就可以在外部增加附加的功能。在面向对象的设计中,通常是通过继承来实现对给定类的功能扩展。” ————–引自<百度百科>

简单case掌握装饰者模式
注意:若是以前没接触过该模式,建议先看下这篇文章《laravel 框架关键技术解析》学习笔记之装饰者模式,再回头看本文效果更佳.
好吧,如果是第一次看装饰者模式,看上面特性”官方阐述”,确实有点不知所云,ok,为方便大家能快速理解该模式,我写了一个”laravel源码简化版”的case,放代码之前给大家说下实现该模式最核心的东西,就靠两个方法,如下:

  1. call_user_func() 不懂就点我
  2. 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;
        }

 

原文:https://segmentfault.com/a/1190000008996458



发表评论

电子邮件地址不会被公开。 必填项已用*标注