title: 基於管道技術實現函數的流式調用
date: 2022-07-04 10:40:00
toc: false
index_img: http://api.btstu.cn/sjbz/?lx=m_dongman&cid=10
category:
- Go
tags: - 順序
- 連接
- 支持
- 請求
- 方法
- 函數
管道(Pipeline)這一術語來源是 Unix 的 Shell 命令行,我們可以使用管道連接符 | 通過組合簡單的命令實現強大的功能,比如我們想要從系統進程列表中篩選出 nginx 進程,可以這麼做:
ps -ef | grep nginx
這裡就是通過管道連接了 ps 和 grep 兩個基本的 Unix 命令,在 ps -ef 的返回結果之上通過 grep nginx 篩選出 nginx 進程。
在函數式編程中,我們也可以借助管道的思想串聯一些簡單的函數構建更加強大的功能,比如最常見的流式函數調用(水流一樣,在面向對象編程中對應的是流接口模式,可以實現鏈式處理)。
這樣一來,每個函數就可以專注於自己要處理的事情,把它做到極致,然後通過組合方式(管道)構建更加複雜的業務功能,這也是符合 SOLID 設計原則的單一職責原則。
我們在 main 函數中通過管道組合 Map-Reduce-Filter
功能模塊,實現這些函數的流式調用:
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 請求處理中間件中也有廣泛的應用。