golang 泛型 middleware 设计模式: 一次只做一件事

golang 泛型 middleware 设计模式: 一次只做一件事

1. 前言

本文主要介绍 在使用 gRPC 和 Gin 框架中常用的 middleware 设计模式

还有几种叫法

  1. 装饰器模式
  2. Pipeline 模式

设计思想:

  1. 10 个 10 行函数, 而不是 1 个 100 行函数
  2. 一次只做一件事, 而不一次做多件事
  3. 单一职责

2. 代码

已生产环境中大量使用, 每日执行千万次

package chain

type Ctx[T any] struct {
    in  T // 数据入参
    fns []func(c *Ctx[T], in T) (err error)
    idx int
}

func NewCtx[T any](in T) *Ctx[T] {
    return &Ctx[T]{
        in:  in,
        idx: -1,
    }
}

func (c *Ctx[T]) Next() (err error) {
    c.idx++
    for ; c.idx < len(c.fns); c.idx++ {
        err = c.fns[c.idx](c, c.in)
        if err != nil {
            return
        }
    }
    return
}

func (c *Ctx[T]) Add(fns ...func(c *Ctx[T], in T) (err error)) {
    c.fns = append(c.fns, fns...)
}

3. test case

package chain

import (
    "fmt"
    "testing"
    "time"
)

type Input struct {
    a int
}

func TestNewCtx(t *testing.T) {

    // 初始化
    in := Input{a: 1}
    c := NewCtx(&in)

    // 添加中间件
    c.Add(ctx1_cost)    // 记录耗时
    c.Add(ctx2_add)     // 数据加工
    c.Add(ctx3_product) // 数据加工2

    // 执行
    err := c.Next()
    if err != nil {
        panic(err)
    }

    // 检查结果
    fmt.Println(in.a)
    if in.a != 4 {
        panic(fmt.Sprintf("expect 4, but got %d", in.a))
    }
}

func ctx1_cost(c *Ctx[*Input], in *Input) (err error) {
    start := time.Now()
    defer func() {
        cost := time.Since(start)
        fmt.Println("cost:", cost)
    }()

    err = c.Next()
    return
}

func ctx2_add(c *Ctx[*Input], in *Input) (err error) {
    in.a += 1
    return
}

func ctx3_product(c *Ctx[*Input], in *Input) (err error) {
    in.a *= 2
    return
}

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容