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
}
相关推荐
子兮曰1 天前
jev-ultrafast 深度解析:7 秒订机票的浏览器 Agent 是如何炼成的
前端·后端·agent
子兮曰1 天前
Jev 爆发一周:7 秒 Agent 背后的 System One 生态与三场争议
前端·后端·ai编程
小羊没烦恼!1 天前
初探性能优化——2个月到4小时的性能提升
java·开发语言·windows·算法·c#
爱勇宝1 天前
ZCode 开源 24 小时:一份没有历史的账本,回答不了"有没有偷代码"
前端·后端·chatglm (智谱)
胡写代码1 天前
别再前后端各写一套表单校验了
java·后端
伞伞悦读1 天前
【第38期】Python 模块与包详解:import、from、模块搜索路径、包结构和 __init__
开发语言·python
ttwuai1 天前
Go开源后台管理系统推荐:怎么按技术栈和边界比较4个官方仓库?
golang·gin
大勇前进1 天前
原生 PHP 还是 Laravel?小项目到底要不要上框架
后端
yuzhi_liu1 天前
我用 LangGraph4j 实现 Multi-Agent Supervisor
后端
alsmile1 天前
Node-RED 之外,国产规则引擎的新方案:基于标准语法,Go 先行实现
后端·开源·go