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

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

1. 前言

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

还有几种叫法

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

设计思想:

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

2. 代码

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

go 复制代码
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

go 复制代码
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
}
相关推荐
大圣数据星球3 小时前
Fluss 写入数据湖实战
大数据·设计模式·flink
思忖小下4 小时前
梳理你的思路(从OOP到架构设计)_设计模式Template Method模式
设计模式·模板方法模式·eit
向前看-7 小时前
验证码机制
前端·后端
超爱吃士力架8 小时前
邀请逻辑
java·linux·后端
AskHarries10 小时前
Spring Cloud OpenFeign快速入门demo
spring boot·后端
isolusion11 小时前
Springboot的创建方式
java·spring boot·后端
zjw_rp12 小时前
Spring-AOP
java·后端·spring·spring-aop
TodoCoder12 小时前
【编程思想】CopyOnWrite是如何解决高并发场景中的读写瓶颈?
java·后端·面试
凌虚13 小时前
Kubernetes APF(API 优先级和公平调度)简介
后端·程序员·kubernetes
机器之心14 小时前
图学习新突破:一个统一框架连接空域和频域
人工智能·后端