装饰器模式以及Laravel框架下的中间件应用

时间:2022-01-31 03:55:14

  Laravel框架的中间件使用:从请求进来到响应返回,经过中间件的层层包装,这种场景很适合用到一种设计模式---装饰器模式。

  装饰器模式的作用,多种外界因素改变对象的行为。使用继承的方式改变行为不太被建议。

  装饰器模式,即是有多个要改变对象的东西(装饰类),这些装饰类均实现一个接口。每个类在实现的接口中再调用构造器(或是setter等方法)传过来的其他装饰类对象的实现。

  简单用一段代码描述下:  

<?php

interface Decorate {
    function getInfo();
}

/**
 * Class DecoateA
 * 初始化一个装饰对象
 */
class DecoateA implements Decorate{

    /**
     * 实现接口
     */
    public function getInfo() {
        echo __CLASS__;
    }
}

/**
 * Class DecoateB
 * 装饰DecoateA 的装饰类
 */
class DecoateB implements Decorate{

    /**
     * DecoateB constructor.
     * @param Decorate $dec
     * 构造器 传过来其他装饰类
     */
    public function __construct(Decorate $dec) {
        $this->dec = $dec;
    }

    /**
     * 实现接口
     */
    public function getInfo() {
        echo __CLASS__;
        $this->dec->getInfo();
    }
}


$obj = new DecoateA();// 初始一个对象
$obj = new DecoateB($obj);// 装饰对象

$obj->getInfo();// DecoateBDecoateA

以上代码,可以简单理解装饰器模式。

 

 

再说Laravel的装饰器模式应用: (这里用静态方法模拟一下)

不像是上边的例子,把装饰类传过去,Laravel用到了大量的闭包Closure代替

一段代码

<?php

interface Step
{
    public static function go(Closure $next);
}

/**
 * Class FirstStep
 * 装饰一
 */
class FirstStep implements Step
{
    public static function go(Closure $next)
    {
        echo __CLASS__." start<br/>";
        $next();
        echo __CLASS__." end<br/>";
    }
}

/**
 * Class SecondStep
 * 装饰二
 */
class SecondStep implements Step
{
    public static function go(Closure $next)
    {
        echo __CLASS__." start<br/>";
        $next();
        echo __CLASS__." end<br/>";
    }
}


function goFun($step, $className)
{
    return function () use ($step, $className)
    {
        return $className::go($step);
    };
}

function then()
{
    $step = ['FirstStep', 'SecondStep'];
    $prepare = function () {
        echo "_init_"."<br/>";
    };
    call_user_func(array_reduce($step, "goFun", $prepare));
}

then();

/**
SecondStep start
FirstStep start
_init_
FirstStep end
SecondStep end
**/

 

这里需要理解下 array_reduct()的用法,第一个参数是被循环的数组,第二个是回调函数(其中回调函数的第一个参数是每次该函数上一次的return值,否则是array_reduce的第三个参数的初始值),第三个参数是初始值。

对于then() 的内部实现,可能开始理解比较困难。可以分拆一下实现:如下代码

// 初始闭包
$initFun = function (){
    echo "_init_"."<br/>";
};

// 相当于array_reduce 第一次执行
$obj = function () use ($initFun) {
    return FirstStep::go($initFun);
};

// 相当于array_reduce 第二次执行。go传递的都是个闭包函数
SecondStep::go($obj);