Laravel框架的中間件使用:從請求進來到響應返回,通過中間件的層層包裝,這種場景很適合用到一種設計模式---裝飾器模式。php
裝飾器模式的做用,多種外界因素改變對象的行爲。使用繼承的方式改變行爲不太被建議。設計模式
裝飾器模式,便是有多個要改變對象的東西(裝飾類),這些裝飾類均實現一個接口。每一個類在實現的接口中再調用構造器(或是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代替this
一段代碼spa
<?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() 的內部實現,可能開始理解比較困難。能夠分拆一下實現:以下代碼code
// 初始閉包 $initFun = function (){ echo "_init_"."<br/>"; }; // 至關於array_reduce 第一次執行 $obj = function () use ($initFun) { return FirstStep::go($initFun); }; // 至關於array_reduce 第二次執行。go傳遞的都是個閉包函數 SecondStep::go($obj);