基于管道技术实现函数的流式调用

管道(Pipeline)这一术语来源是 Unix 的 Shell 命令行,我们可以使用管道连接符 | 通过组合简单的命令实现强大的功能,比如我们想要从系统进程列表中筛选出 nginx 进程,可以这么做:

1
ps -ef | grep nginx 

这里就是通过管道连接了 ps 和 grep 两个基本的 Unix 命令,在 ps -ef 的返回结果之上通过 grep nginx 筛选出 nginx 进程。

在函数式编程中,我们也可以借助管道的思想串联一些简单的函数构建更加强大的功能,比如最常见的流式函数调用(水流一样,在面向对象编程中对应的是流接口模式,可以实现链式处理)。

这样一来,每个函数就可以专注于自己要处理的事情,把它做到极致,然后通过组合方式(管道)构建更加复杂的业务功能,这也是符合 SOLID 设计原则的单一职责原则。

我们在 main 函数中通过管道组合 Map-Reduce-Filter 功能模块,实现这些函数的流式调用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
package main

import (
"fmt"
"log"
)

type user struct {
name string
age int
}

func main() {
var users = []user{
{
name: "xmp",
age: 11,
},
{
name: "wlm",
age: 24,
},
{
name: "wjf",
age: 30,
},
{
name: "lm",
age: 50,
},
}
// 处理器函数按照声明的先后顺序依次调用
result := sumAge(users, filterAge, mapAgeToSlice)

fmt.Println("总年龄(排除大于40小于20):", result)
}

func filterAge(users []user) interface{} {
var result []user
for _, user := range users {
if user.age < 40 && user.age > 20 {
result = append(result, user)
}
}
return result
}

func mapAgeToSlice(users []user) interface{} {
var result []int
for _, user := range users {
result = append(result, user.age)
}
return result
}

func sumAge(users []user, pips ...func([]user) interface{}) (result int) {
var reusltSlice []int
for _, f := range pips {
runResult := f(users)
switch runResult.(type) {
case []user:
users = runResult.([]user)
case []int:
reusltSlice = runResult.([]int)
}
}
if len(reusltSlice) == 0 {
log.Fatalln("没有在管道中加入 mapAgeToSlice 方法")
}

for _, age := range reusltSlice {
result += age
}
return result
}

这里,我们引入了一个 user 结构体替代字典类型,让代码更加简洁,可读性更好,关于结构体类型,学院君将在下个章节 Go 类型系统中给大家详细介绍。

然后我们将 Filter 和 Map 函数中的闭包函数取消掉了,改为直接在代码中实现,以便精简代码,为了便于通过管道统一声明 Filter 和 Map 函数,将他们的返回值声明成了空接口 interface{} 表示可以返回任何类型。

接下来重点来看 Reduce 函数 sumAge 的实现,这里,我们将其第二个参数声明为了变长参数类型,表示支持传递多个处理函数,这些处理器函数按照声明的先后顺序依次调用,由于这些处理函数的返回值类型被声明为了空接口,所以需要在运行时动态对它们的返回值类型做检测,并赋值给指定变量,以便程序可以按照我们期望的路径执行下去,而不会因为类型问题报错退出。

通过管道,我们可以更优雅地实现 Filter->Map->Reduce 的流式调用。此外,管道技术在 HTTP 请求处理中间件中也有广泛的应用。


基于管道技术实现函数的流式调用
https://www.biuaxia.cn/2022/07/04/10/40/00.html
作者
biuaxia
发布于
2022年7月4日
许可协议