和Java里面推崇的设计模式类似,Go语言也存在许多编程模式,学习这些编程模式,不仅可以让我们能了解优秀的设计思想,而且也能帮助Go程序员在工作中在写出简洁、优雅的代码实现。
Pipe-Filter
- 非常适合与数据处理及数据分析系统 (比如审批流,订单流等)
- Filter 封装数据处理的功能
- 松耦合: Filter只跟数据(格式) 耦合
- Pipe用于连接 Filter 传递数据或者在异步处理过程中缓冲数据流
- 进程内同步调用时,pipe 演变为数据在方法调用间传递
例子:
题目:将一个包含多个数字的字符串求和,数字以英文,相隔。
比如 :
输入:“1,2,3”
输出:6
我们可以把数据处理拆分为几个步骤:
- 把"1,2,3" 拆为 字符串数组 [“1”,“2”,“3”]
- 字符串数组 [“1”,“2”,“3”] 转为 整型数组 [1,2,3]
- 整型数组 [1,2,3] 求和
下面我们用Pipe-Filter 来实现这个题目。
- filter.go 抽象的Filter接口,后序的数据处理只要实现该接口即可。
package pipeFilter
type Request interface{} // 请求
type Response interface{} // 响应
// 抽象的 Filter 接口
type Filter interface {
Process(data Request) (Response, error)
}
- split_filter.go 数据处理-字符串分隔
package pipeFilter
import (
"errors"
"strings"
)
var SplitFilterWrongFormatError = errors.New("input data should be string")
type SplitFilter struct {
delimiter string // 分隔符
}
func NewSplitFilter(delimter string) *SplitFilter {
return &SplitFilter{delimter}
}
// 数据处理-字符串分隔
func (sf *SplitFilter) Process(data Request) (Response, error) {
// 检查数据格式/类型,是否可处理
str, ok := data.(string)
if !ok {
return nil, SplitFilterWrongFormatError
}
parts := strings.Split(str, sf.delimiter)
return parts, nil
}
3.to_int_filter.go 数据处理-字符串数组转为int数组
package pipeFilter
import (
"errors"
"strconv"
)
var ToIntFilterWrongFormatError = errors.New("input data should be []string")
type ToIntFilter struct {
}
func NewToIntFilter() *ToIntFilter {
return &ToIntFilter{}
}
// 数据处理-字符串数组转为int数组
func (sf *ToIntFilter) Process(data Request) (Response, error) {
parts, ok := data.([]string)
if !ok {
return nil, ToIntFilterWrongFormatError
}
ret := []int{}
for _, part := range parts {
s, err := strconv.Atoi(part)
if err != nil {
return nil, err
}
ret = append(ret, s)
}
return ret, nil
}
4.sum_filter.go 数据处理-int数组求和
package pipeFilter
import (
"errors"
)
var SumFilterWrongFormatError = errors.New("input data should be []int")
type SumFilter struct {
}
func NewSumFilter() *SumFilter {
return &SumFilter{}
}
// 数据处理-int数组求和
func (sf *SumFilter) Process(data Request) (Response, error) {
elems, ok := data.([]int)
if !ok {
return nil, SumFilterWrongFormatError
}
ret := 0
for _, elem := range elems {
ret += elem
}
return ret, nil
}
这里已经实现每个数据处理环节,我们只需要将各个环节串联起来。
5.straight_pipeline.go 组合各个Filter,并对外提供处理方法。
package pipeFilter
type StraightPipeline struct {
Name string // Pipeline的名称
filters []Filter // Filter过滤器列表
}
func NewStraightPipeline(name string) *StraightPipeline {
return &StraightPipeline{Name: name, filters: []Filter{}}
}
// 注册Filter
func (f *StraightPipeline) RegisterFilter(filters ...Filter) {
f.filters = append(f.filters, filters...)
}
// 执行Filter处理逻辑
func (f *StraightPipeline) Process(data Request) (Response, error) {
var ret interface{}
var err error
for _, filter := range f.filters {
ret, err = filter.Process(data)
if err != nil {
return ret, err
}
data = ret
}
return ret, err
}
6.单测如下
package pipeFilter
import "testing"
func TestStraightPipeline(t *testing.T) {
data := "1,2,3"
spliter := NewSplitFilter(",")
converter := NewToIntFilter()
sum := NewSumFilter()
sp := NewStraightPipeline("p1")
sp.RegisterFilter(spliter, converter, sum)
ret, err := sp.Process(data)
if err != nil {
t.Fatal(err)
}
if ret != 6 {
t.Fatalf("The expected is 6, but the actual is %d", ret)
}
}
总结:Pipe-Filter 非常适合数据处理和多环节场景。(比如审批流和订单流等等)
参考: