水管太长,只要有一处破了,就会漏水了,而且不利于复杂环境弯曲转折使用。所以我们都会把水管分成很短的一节一节管道,然后最大化的让管道大小作用不同,因地制宜,组装在一起,满足各种各样的不同需求。
由此得出 Pipeline 的设计模式,就是将复杂冗长的流程 (processes) 截成各个小流程,小任务。每个最小量化的任务就可以复用,通过组装不同的小任务,构成复杂多样的流程 (processes)。
最后将「输入」引入管道,根据每个小任务对输入进行操作 (加工、过滤),最后输出满足需要的结果。
今天主要学习学习「Pipeline」,顺便推荐一个 PHP 插件:league/pipeline。
gulp第一次知道「pipe」的概念,来自 gulp 的使用。
gulp 是基于 NodeJS 的自动任务运行器,她能自动化地完成Javascript、sass、less 等文件的测试、检查、合并、压缩、格式化、浏览器自动刷新、部署文件生成,并监听文件在改动后重复指定的这些步骤。在实现上,她借鉴了 Unix 操作系统的管道 (pipe) 思想,前一级的输出,直接变成后一级的输入,使得在操作上非常简单。
var gulp = require('gulp');
var less = require('gulp-less');
var minifyCSS = require('gulp-csso');
var concat = require('gulp-concat');
var sourcemaps = require('gulp-sourcemaps');
gulp.task('css', function(){
return gulp.src('client/templates
public function pipe(callable $operation): PipelineInterface;
}
interface StageInterface
{
public function __invoke($payload);
}
该接口主要是利用链式编程的思想,不断添加管道「pipe」,然后增加一个魔术方法,来让传入的参数运转起来。
先看看这个魔术方法的作用:
> mixed __invoke ([ $... ] )
> 当尝试以调用函数的方式调用一个对象时,__invoke() 方法会被自动调用。
>
> 参考来自:http://php.net/manual/zh/language.oop5.magic.php
> 如:
>
<?php
class CallableClass
{
function __invoke($x) {
var_dump($x);
}
}
$obj = new CallableClass;
$obj(5);
var_dump(is_callable($obj));
?>
返回结果:
int(5)
bool(true)
Pipeline
<?php
declare(strict_types=1);
namespace LeaguePipeline;
class Pipeline implements PipelineInterface
{
private $stages = [];
private $processor;
public function __construct(ProcessorInterface $processor = null, callable ...$stages)
{
$this->processor = $processor ?? new FingersCrossedProcessor;
$this->stages = $stages;
}
public function pipe(callable $stage): PipelineInterface
{
$pipeline = clone $this;
$pipeline->stages[] = $stage;
return $pipeline;
}
public function process($payload)
{
return $this->processor->process($payload, ...$this->stages);
}
public function __invoke($payload)
{
return $this->process($payload);
}
}
其中核心类 Pipeline 的作用主要就是两个:
- 添加组装各个管道「pipe」;
- 组装后,引水流动,执行 process($payload),输出结果。
Processor
接好各种管道后,那就要「引水入渠」了。该插件提供了两个基础执行类,比较简单,直接看代码就能懂。
// 按照 $stages 数组顺利,遍历执行管道方法,再将结果传入下一个管道,让「水」一层层「流动」起来
class FingersCrossedProcessor implements ProcessorInterface
{
public function process($payload, callable ...$stages)
{
foreach ($stages as $stage) {
$payload = $stage($payload);
}
return $payload;
}
}
// 增加一个额外的「过滤网」,经过每个管道后的结果,都需要 check,一旦满足则终止,直接输出结果。
class InterruptibleProcessor implements ProcessorInterface
{
private $check;
public function __construct(callable $check)
{
$this->check = $check;
}
public function process($payload, callable ...$stages)
{
$check = $this->check;
foreach ($stages as $stage) {
$payload = $stage($payload);
if (true !== $check($payload)) {
return $payload;
}
}
return $payload;
}
}
interface ProcessorInterface
{
public function process($payload, callable ...$stages);
}
我们完全也可以利用该接口,实现我们的方法来组装管道和「过滤网」。
PipelineBuilder
最后提供了一个 Builder,这个也很好理解:
class PipelineBuilder implements PipelineBuilderInterface
{
private $stages = [];
public function add(callable $stage): PipelineBuilderInterface
{
$this->stages[] = $stage;
return $this;
}
public function build(ProcessorInterface $processor = null): PipelineInterface
{
return new Pipeline($processor, ...$this->stages);
}
}
interface PipelineBuilderInterface
{
public function add(callable $stage): PipelineBuilderInterface;
public function build(ProcessorInterface $processor = null): PipelineInterface;
}
总结
无论是对不同技术的横向理解,还是基于 Laravel 或者某些开源插件,我们都能学习到技术之上的通用原理和方法。再将这些原理和方法反作用于我们的实际代码开发中。
最近闲来没事,自己参考 Laravel 去写个简易框架,也将LeaguePipeline 引入到框架中使用。
「未完待续」



