【Go设计模式】Pipeline

637 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 19 天,点击查看活动详情

Pipeline

Pipeline是一种常见的设计模式,用于将多个处理步骤连接起来,以便数据可以通过它们进行转换和处理。

在Pipeline模式中,数据从一个处理步骤流经下一个处理步骤,以此类推,直到到达最终的处理步骤。每个处理步骤只处理数据的一部分,然后将其传递给下一个步骤。

这种模式的好处在于,它可以让我们将复杂的处理任务分解成一系列相对简单的任务,从而更容易进行维护和调试。此外,由于每个步骤只处理数据的一部分,因此可以并行处理多个步骤,从而加速整个处理过程。

在实际应用中,Pipeline模式广泛应用于数据处理、计算机视觉、自然语言处理等领域。例如,在机器学习中,我们可以将数据预处理、特征提取、模型训练等步骤组合在一起形成Pipeline,以进行模型训练和推理。在图像处理中,我们可以将图像分割、特征提取、分类等步骤组合在一起形成Pipeline,以进行图像识别和分类。

Pepeline 实现

定义基本pipeline节点的接口filter相当于一次计算,

type Request interface{}

type Response interface{}

type Pipe interface {
   Process(data Request) (Response, error)
}

定义pipeline组装器,组装多任务成pipeline并可调用。

func NewPipeline(name string, filters ...Pipe) *Pipeline {
   return &Pipeline{
      Name:  name,
      Pipes: &filters,
   }
}

type Pipeline struct {
   Name  string
   Pipes *[]Pipe
}

func (p *Pipeline) Process(data Request) (Response, error) {
   var ret interface{}
   var err error
   for _, pipe := range *p.Pipes {
      ret, err = pipe.Process(data)
      if err != nil {
         return ret, err
      }
      data = ret
   }
   return ret, err
}

Pipeline调用

例如我们要实现一个对字符串的操作,先分割,再各自转为int,再求和返回结果:

  1. 定义分割器
var SplitPipeWrongFormatError = errors.New("input data should be string")

type SplitPipe struct {
   delimiter string
}

func NewSplitPipe(delimiter string) *SplitPipe {
   return &SplitPipe{delimiter}
}

func (sf *SplitPipe) Process(data Request) (Response, error) {
   str, ok := data.(string) //检查数据格式/类型,是否可以处理
   if !ok {
      return nil, SplitPipeWrongFormatError
   }
   parts := strings.Split(str, sf.delimiter)
   return parts, nil
}
  1. 定义类型转换器
var ToIntPipeWrongFormatError = errors.New("input data should be []string")

type ToIntPipe struct {
}

func NewToIntPipe() *ToIntPipe {
   return &ToIntPipe{}
}

func (tif *ToIntPipe) Process(data Request) (Response, error) {
   parts, ok := data.([]string)
   if !ok {
      return nil, ToIntPipeWrongFormatError
   }
   ret := []int{}
   for _, part := range parts {
      s, err := strconv.Atoi(part)
      if err != nil {
         return nil, err
      }
      ret = append(ret, s)
   }
   return ret, nil
}
  1. 定义求和器
var SumPipeWrongFormatError = errors.New("input data should be []int")

type SumPipe struct {
}

func NewSumPipe() *SumPipe {
   return &SumPipe{}
}

func (sf *SumPipe) Process(data Request) (Response, error) {
   elems, ok := data.([]int)
   if !ok {
      return nil, SumPipeWrongFormatError
   }
   ret := 0
   for _, elem := range elems {
      ret += elem
   }
   return ret, nil
}
  1. 初始化,拼接,调用
func TestStraightPipeline(t *testing.T) {
   spliter := NewSplitPipe(",")
   converter := NewToIntPipe()
   sum := NewSumPipe()
   sp := NewPipeline("p1", spliter, converter, sum)
   ret, err := sp.Process("1,2,3")
   if err != nil {
      t.Fatal(err)
   }
   if ret != 6 {
      t.Fatalf("The expected is 6, but the actual is %d", ret)
   }
}