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
}
相关推荐
东风破_6 小时前
ESLint 是什么?为什么你的项目需要它?
前端·后端·代码规范
嘻哈∠※6 小时前
0061基于 SpringBoot 的投稿与稿件处理系统设计与实现
java·spring boot·后端
WWJA王文举6 小时前
I²C通信完整流程详解:START、地址、ACK、数据、Repeated START和STOP一次讲透
c语言·开发语言
卷无止境6 小时前
在 awesome-fastapi 里,哪些库值得一看?
后端·python
zhanghaha13146 小时前
Python进阶教程:6_JSON 数据解析 —— 新手完全指南
开发语言·python·json
圣殿骑士-Khtangc7 小时前
Go字符串高效拼接性能对比与底层原理分析
服务器·前端·golang
卷无止境7 小时前
FastAPI 的Admin面板生态
后端·python
qq_448011168 小时前
C语言中的动态内存分配
c语言·开发语言·php
汉字萌萌哒8 小时前
2024CSP-J入门级C++真题详解
开发语言·c++
arbboter9 小时前
【网络工具】NetProxy网络代理用户手册
开发语言·网络·c#·网络代理·网络异常·代理工具