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
}
相关推荐
用户8356290780513 分钟前
Python 自动化 Word 文本框处理:创建、定位、填充内容与管理
后端·python
网渡科技15 分钟前
向量数据库选型指南:RAG场景下的性能对比与调优策略
后端
riverNijika35 分钟前
项目学习笔记:C++基础同步输出日志库
后端
Lyra_Infra40 分钟前
OpenClaw 升级及 Channel 安装故障排查与遗留问题分析
后端·npm
江小渔1 小时前
训练工程与训练平台入门 31 安装 Kubeflow Trainer,并跑第一个 TrainJob
后端
用户938515635071 小时前
从 0 拆解一个 Next.js 笔记系统:npx、App Router、RSC 与组件规划全记录
前端·后端·全栈
fatcoder2 小时前
玩转Docker 06 — 容器网络
前端·后端·docker
feng尘2 小时前
深入浅出 Java:ThreadLocal 为什么会产生内存泄漏?
后端
xzlAwin3 小时前
Win10安装Go语言多版本管理器g工具
开发语言·golang
懒人wsh3 小时前
不上悲观锁也不上 Redis:一个 AI 平台的钱包并发是怎么搞的
后端