極簡設計模式-裝飾模式

long2ge發表於2021-12-04

定義

動態地給一個物件增加一些額外的職責,就增加物件功能來說,裝飾模式比生成子類實現更為靈活。這樣可以給某個物件而不是整個類新增一些功能。

設計的原則和思想

  1. 解耦的是裝飾者和被裝飾者。
  2. 不變部分是被裝飾者,變化部分是裝飾者。
  3. 核心思想是對物件自身功能進行增強。

一句話概括設計模式

透過組合的方式,增強物件自身的功能,並且支援多個裝飾器巢狀使用。

經典裝飾者模式

結構中包含的角色

  1. Component(抽象構件)
  2. ConcreteComponent(具體構件)
  3. Decorator(抽象裝飾類)
  4. ConcreteDecorator(具體裝飾類)

最小可表達程式碼

// 抽象構件類
abstract class Component
{
    public abstract function display();
}

// 具體構件類
class ConcreteComponent extends Component
{
    public function display()
    {
        echo ' 具體構件 ';
    }
}

// 抽象裝飾類
abstract class ComponentDecorator extends Component
{
    protected $component;

    public function __construct(Component $component)
    {  
        $this->component = $component;
    }
}

// 具體裝飾類
class ConcreteDecorator extends  ComponentDecorator
{
    public function display()
    {
        echo ' 裝飾前的行為 ';
        $this->component->display();
        echo ' 裝飾後的行為 ';
    }
}

$decorator = new ConcreteDecorator(new ConcreteComponent);
$decorator->display();

Laravel管道閉包方式的裝飾者模式

結構中包含的角色

  1. MiddlewareManager (中介軟體管理類/修飾者管理類)
  2. Middleware (抽象中介軟體/抽象裝飾者)
  3. ConcreteMiddleWare (具體中介軟體/具體裝飾者)
  4. Controller (控制器/被裝飾者)
  5. Request (請求類)

最小可表達程式碼

class MiddlewareManager
{
    // 請求
    protected $request;

    // 中介軟體 middlewares
    protected $middlewares = [];

    // 設定請求
    public function send($request)
    {
        $this->request = $request;

        return $this;
    }

    // 設定中介軟體
    public function through($middlewares)
    {
        $this->middlewares = is_array($middlewares) ? $middlewares : func_get_args();

        return $this;
    }

    // 輸入需要執行的控制器,並且開始裝飾控制器
    public function then(Closure $controllerClosure)
    {
        $middlewareClosure = array_reduce(
            array_reverse($this->middlewares), $this->carry(), $this->prepareDestination($controllerClosure)
        );

        // 很多人不太熟悉 array_reduce 的用法,這裡附上 foreach 的寫法,方便理解。
        // $middlewareClosure = $this->prepareDestination($controllerClosure);
        // foreach (array_reverse($this->middlewares) as $middleware) {
        //     $middlewareClosure = function ($request) use ($middlewareClosure, $middleware) {
        //         return $middleware->handle($request, $middlewareClosure);
        //     };
        // }

        return $middlewareClosure($this->request);
    }

    // 設定請求作為引數傳給控制器
    protected function prepareDestination(Closure $controllerClosure)
    {
        return function ($request) use ($controllerClosure) {
            return $controllerClosure($request);
        };
    }

    // 執行多箇中介軟體的方法
    protected function carry()
    {
        return function ($controllerClosure, $middleware) {
            return function ($request) use ($controllerClosure, $middleware) {
                return $middleware->handle($request, $controllerClosure);
            };
        };
    }
}

class Request{}

interface Middleware
{
    public function handle(Request $request, Closure $controllerClosure);
}

class ConcreteMiddleWare implements Middleware
{
    public function handle(Request $request, Closure $controllerClosure)
    {
        var_dump("前");

        $response = $controllerClosure($request);

        var_dump("後");

        return $response;
    }
}

class Controller
{
    public function test(Request $request)
    {
        var_dump("執行控制器的方法");

        return ['status' => 200];
    }
}

$response = (new MiddlewareManager)
    ->send(new Request)
    ->through([new ConcreteMiddleWare])
    ->then(function ($request) {
        return (new Controller)->test($request);
    });

var_dump($response);

優點

  1. 你可以在執行時新增或刪除物件的功能。
  2. 你可以用多個裝飾封裝物件來組合幾種行為。
  3. 裝飾類和被裝飾類可以獨立發展,不會相互耦合。
  4. 透過組合來替代繼承,使用物件之間的關聯關係取代類之間的繼承關係。

缺點

  1. 在封裝器棧中刪除指定裝飾器比較困難。
  2. 實現行為不受裝飾棧順序影響的裝飾比較困難。
  3. 比繼承更容易出錯,排錯也比較困難,程式碼較為繁瑣。
  4. 多層裝飾比較複雜。

何時使用

  1. 擴充套件一個類的功能。
  2. 動態增加功能。
  3. 當不能採用繼承的方式(final)對系統進行擴充套件或者採用繼承不利於系統擴充套件和維護(大量獨立擴充套件)

實際應用場景

  1. Laravel的中介軟體。
  2. 漢堡包,加番茄醬,加牛肉,加雞蛋。。。
  3. Java的IO流。
本作品採用《CC 協議》,轉載必須註明作者和本文連結
Long2Ge

相關文章