PHP框架模式1:管道模式

计算机领域内管道模式其本质大体和现实领域的管道系统相同:栽荷从从抽象的管道的一端流入,经过一系列的处理,最终从管道的另一端流出。

管道模式的形式有很多种,常见的有:

  • 进程间管道,通常用于兄弟进程、亲缘进程、父子进程及子进程之间的通讯;
  • 凹型管道,用于客户端与服务端的业务流程处理及通讯;
  • 直线型管道,用于客户端或服务端的一般流程性业务处理;

直线型的管道模式又可以分为自治型管道模式和约定型管道模式, 而绝大多数框架中提供的管道模式都是自治直线型管道,它通常被在同一个项目内结束的业务过程所使用,从而动态地组合碎片化的业务流程。。

自治型管道中的所有处理对象都是由自己添加,内部结构以及前后衔接都是由自己控制,以下示例参考Laravel的实现:

<?php

class Pipeline
{
    /**
     * 栽荷
     *
     * @var mixed
     */
    protected $passable;

    /**
     * 管道
     *
     * @var array
     */
    protected $pips = [];

    /**
     * 管道委托方法
     *
     * @var string
     */
    protected $method = 'handle';

    /**
     * 设置管道要调用的方法
     *
     * @param string $method
     * @return $this
     */
    public function via(string $method)
    {
        $this->method = $method;

        return $this;
    }

    /**
     * 发送栽荷
     *
     * @param $passable
     * @return $this
     */
    public function send($passable)
    {
        $this->passable = $passable;

        return $this;
    }

    /**
     * 运行管道并执行回调
     *
     * @param Closure $destination
     * @return mixed
     */
    public function then(Closure $destination)
    {
        $pipeline = array_reduce(
            array_reverse($this->pipes()), $this->carry(), $this->prepareDestination($destination)
        );

        return $pipeline($this->passable);
    }

    /**
     * 运行回调并返回栽荷
     *
     * @return mixed
     */
    public function thenReturn()
    {
        return $this->then(function ($passable) {
            return $passable;
        });
    }

    /**
     * 设置管道数组
     *
     * @param $pipes
     * @return $this
     */
    public function through($pipes)
    {
        $this->pipes = is_array($pipes) ? $pipes : func_get_args();

        return $this;
    }

    /**
     * 管道委托调用
     *
     * @return Closure
     */
    protected function carry()
    {
        return function ($stack, $pipe) {
            return function ($passable) use ($stack, $pipe) {
                if (is_callable($pipe)) {
                    // 管道可调用,例如方法,则直接调用
                    return $pipe($passable, $stack);
                } else if (!is_object($pipe)) {
                    [$name, $parameters] = $this->parsePipeString($pipe);
                    $pipe       = new $name();
                    $parameters = array_merge([$passable, $stack], $parameters);
                } else {
                    $parameters = [$passable, $stack];
                }
                return method_exists($pipe, $this->method) ? $pipe->{$this->method}(...$parameters) : $pipe(...$parameters);
            };
        };
    }

    /**
     * 从字符串获取类名和参数
     *
     * @param $pipe
     * @return array
     */
    protected function parsePipeString($pipe)
    {
        [$name, $parameters] = array_pad(explode(':', $pipe, 2), 2, []);

        if (is_string($parameters)) {
            $parameters = explode(',', $parameters);
        }

        return [$name, $parameters];
    }
}

相比较传统的new pipeline()->pipe($callable)->pipe($obj)->process($passable) 这种链式调用,Laravel的实现非常的巧妙,它先反转了存储管道的数组,将最后加入管道封装为函数,作为上一个管道封装函数的参数, 直到最先加入的管道, 这样就形成了一条调用链, 而回调则作为array_reduce的第一个参数一直向下传递。

使用自治型管道模式

<?php

function filter1()
{ 
    $passable[] = '采蘑菇的小姑娘';
    return $passable;
}

class filter2
{
    public function handle($passable, $message)
    {
        $passable[] = $message;
        return $passable;
    }
}

function callback($passable)
{
    var_dump($passable);
}

$pipes = [
     filter1(),
    'filter2:爱吃鱼的大脸猫'
];
(new Pipeline())->send(['载荷'])->through($pipes)->then(callback());

自治直线型管道模式与责任链模式及过滤器模式的联系与区别

管道模式可以实现责任链和过滤器模式所实现的功能,但管道模式作为框架模式,它有更高的层级,所以管道模式通常用于与其他开发者之间的共识的约定, 例如Laravel的中间件。

发表回复

您的电子邮箱地址不会被公开。