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
}
相关推荐
冰_河1 小时前
QPS从300到3100:我靠一行代码让接口性能暴涨10倍,系统性能原地起飞!!
java·后端·性能优化
JavaGuide4 小时前
7 道 RAG 基础概念知识点/面试题总结
前端·后端
桦说编程4 小时前
从 ForkJoinPool 的 Compensate 看并发框架的线程补偿思想
java·后端·源码阅读
格砸5 小时前
从入门到辞职|从ChatGPT到OpenClaw,跟上智能时代的进化
前端·人工智能·后端
蝎子莱莱爱打怪5 小时前
GitLab CI/CD + Docker Registry + K8s 部署完整实战指南
后端·docker·kubernetes
哈密瓜的眉毛美6 小时前
零基础学Java|第三篇:DOS 命令、转义字符、注释与代码规范
后端
用户60572374873086 小时前
AI 编码助手的规范驱动开发 - OpenSpec 初探
前端·后端·程序员
哈密瓜的眉毛美6 小时前
零基础学Java|第二篇:Java 核心机制与第一个程序:从 JVM 到 Hello World
后端
静水流深_沧海一粟6 小时前
04 | 别再写几十个参数的构造函数了——建造者模式
设计模式