摘要
计算机基础的同学估计对管道这个词都不陌生了,尤其是在Linux系统当中,管道操作符已经被广泛的使用,并给我们的变成带来了极大的便利。前端领域比较注明的脚手架“gulp”也是以其管道操作著称。
今天我们就来一步步抽丝剥茧,看看在前端领域的“管道数据流”要如何设计。
一、前言
有计算机基础的同学估计对管道这个词都不陌生了,尤其是在Linux系统当中,管道操作符已经被广泛的使用,并给我们的变成带来了极大的便利。管道操作通常分为单向管道和双向管道,当数据从上一节管道流向下一节管道时,我们的数据将会被这节管道进行一定的加工处理,处理完毕后送往下一节管道,依次类推,这样就可以对一些原始的数据在不断的管道流动中进行不断的加工,最后得到我们想要的目标数据。
在我们日常编程开发过程中,也可以尝试使用管道数据的概念,对我们的程序架构进行一定的优化,让我们程序的数据流动更加清晰明了,并可以让我们像是流水线一样,每个管道专门负责各自的工作对数据源进行一次粗加工,达到职责分明与程序解耦的目的。
二、程序设计
现在我们使用Typescript实现一个基础的管道类的设计,我们今天使用的管道是单向管道。
2.1 Pipe-转接头
顾名思义,转接头就是需要将不同的多节管道连接在一起成为一整条管道的连接口,通过这个连接头,我们可以控制数据的流向,让数据流向他真正该去的的地方。
首先,我们来设计一下我们的转接头的类型结构:
type Pipeline<T = any> = {
/**
* 将多节管道链接起来
* e.g.
* const app = new BaseApp();
* app.pipe(new TestApp1()).pipe(new TestApp2()).pipe(new TestApp3()).pipe(new Output()).pipe(new End())
* @param _next
*/
pipe(_next: Pipeline<T>): Pipeline<T>;
};
上述代码描述了一个支持管道数据的类需要有怎样的一个转接头,在程序设计中,我们的转接头其实就是一个函数,用于将多节管道相互链接。
从上面的代码大家可以看出,为了程序的高复用,我们选择对管道中传输的数据类型进行泛型化,这样,我们再具体实现某一个程序时,便可更加灵活的使用其中类型,例如:
// 时间类型的管道
type DatePipeline = Pipeline<Date>
// 数组类型的管道
type ArrayPipeLine = Pipeline<string[]>
// 自定义数据类型的管道
type CustomPipeLine = Pipeline<{
name: string, age: number}>
除此之外,我们这个函数的传入参数和返回值也是有讲究的,从上面的代码可以看出,我们接收一个管道类型的数据,又返回一个管道类型的数据。其中,参数中传入的便是下一节管道,这样,我们就把两节管道连接到了一起。之所以要返回一个管道类型的数据,是为了让我们使用时可以链式调用,更符合管道数据的设计理念,如:
const app = new AppWithPipleline();
app.pipe(new WorkerPipeline1())
.pipe(new WorkerPipeline2())
.pipe(new WorkerPipeline3())
.pipe(new WorkerPipeline4())
也就是说,我们返回的,其实也是下一节管道的引用。
2.2 Push-水泵
有了转接头之后,我们还需要一个“水泵”将我们的数据源源不断地推送到不同的管道,最终到达目标点。
type Pipeline<T = any> = {
/**
* 实现该方法可以将数据通过管道一层层传递下去
* @param data
*/
push(data: T[]): Promise<