Go泛型的应用

一、一个简单的业务背景

我们有个业务层叫service, 两个方法,它们的返回值不同。

虽然不太一样,但是结构一样, 都是 func() (T, error)

Go 复制代码
type Service struct{}

func (s *Service) GetUser() (string, error) {
    return "alice", nil
}

func (s *Service) GetAge() (int, error) {
    return 18, nil
}

这时候假如我们需要一个"通用重试中间件",就是在业务方法调用失败的时候, 能重试一次。对应到真实业务,就好比我们每个方法都是调用第三方平台接口进行操作,如果操作失败能重试,或者token失效,能刷新一次token在重试等等。

二、非泛型方式实现

我们可以实现一个通用的中间件,它的参数就是上面的业务方法,先去执行业务方法,如果失败,在执行一次。

我们就得抽象出下面的函数类型

Go 复制代码
type BizFuncAny func() (any, error)

中间件

Go 复制代码
func WithRetry(fn BizFunc) BizFunc {
    return func() (any, error) {
        resp, err := fn()
        if err == nil {
            return resp, nil
        }

        // 模拟:失败后重试一次
        return fn()
    }
}

当要调用GetUser(),我们要手动包一层才能把GetUser()变为符合上面定义的函数类型

Go 复制代码
getUser := func() (any, error) {
    return svc.GetUser()
}

使用

Go 复制代码
func main() {
    svc := &Service{}

    // GetUser 被包成 BizFunc
    getUser := WithRetry(func() (any, error) {
        return svc.GetUser()
    })

    // GetAge 被包成 BizFunc
    getAge := WithRetry(func() (any, error) {
        return svc.GetAge()
    })

    u, _ := getUser()
    a, _ := getAge()

    // ⚠️ 必须类型断言
    fmt.Println(u.(string))
    fmt.Println(a.(int))
}

这个方式有几个缺点:

1、对于svc.GetUser等业务方法必须包一层,这样才能符合WithRetry的参数的函数签名。所以每次对新的业务方法使用的时候,都要手写闭包。

2、返回值变为any,如果我们要处理这个返回值,就得进行类型断言。

3、编译期无法发现类型错误。

4、改 DTO 会导致运行时 panic。

三、使用泛型的简单例子

我们定义一个"业务函数类型",泛型Resp:表示不同业务返回不同类型。

Go 复制代码
type BizFunc[Resp any] func() (Resp, error)

实现一个"通用重试中间件"

Go 复制代码
func WithRetry[Resp any](
    fn BizFunc[Resp],
) BizFunc[Resp] {
    return func() (Resp, error) {
        resp, err := fn()
        if err == nil {
            return resp, nil
        }

        // 这里假装"重试一次"
        return fn()
    }
}

使用这个泛型函数

Go 复制代码
func main() {
    svc := &Service{}

    getUser := WithRetry(svc.GetUser)
    getAge := WithRetry(svc.GetAge)

    u, _ := getUser()
    a, _ := getAge()

    fmt.Println(u, a) // alice 18
}
相关推荐
小徐不徐说4 小时前
序列化与反序列化:把 C++ 对象可靠地保存下来
开发语言·数据库·c++·php
ZhengEnCi4 小时前
模型训练完整流程:从数据准备到模型上线的 10 个关键步骤(全文可跑)
后端·算法
ragnwang5 小时前
Notes: Python pybind11 import C++ module
开发语言·c++·python
王中阳Go5 小时前
后端转 Agent 岗面试怎么答:8 个高频问题与应答口径
人工智能·后端·python·大语言模型
Wang's Blog5 小时前
Java 项目实战: 交友项目 - 搜索附近的功能概述和分析
java·开发语言·交友
程序猿DD5 小时前
OctaFuse Gateway 2.18.0:MCP 接入支持 OAuth,供应商粘性绑定可视化管理
后端
骑着蜗牛撵大象3275 小时前
大模型 Agent 的沙箱护栏:gVisor 隔离、出口白名单与审计闭环
java·开发语言·jvm·安全·agent沙箱·gvisor·microvm
Dxy12393102166 小时前
JS DES 加密详解
开发语言·javascript·arcgis
充值内卷6 小时前
我给桌面加了个鱼缸:C++ 写后端,WebView2 里养一座 Three.js 小岛
开发语言·javascript·c++
云和数据.ChenGuang6 小时前
深入解析 Python 的 dir():从用法到 CPython 源码实现
开发语言·人工智能·python·langchain