极简设计模式-装饰模式

装饰模式 - Decorator Pattern

定义

动态地给一个对象增加一些额外的职责,就增加对象功能来说,装饰模式比生成子类实现更为灵活。这样可以给某个对象而不是整个类添加一些功能。

设计的原则和思想

  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
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!