这篇文章源于偶然看到的一篇文章 # Golang 技巧之默认值设置的高阶玩法,它讲的是 GRPC 中的设计模式。而我没有研究过 GRPC 源码,看起来稍显陌生。但好在手头上有 Gitaly 源码,算是稍微熟悉它的源码,因此想看看这个项目里面是不是也应用到了前面文章里讲的高阶用法,简单搜索一下源码后,发现这种代码模式还挺多的,于是趁热记录了其中一小段阅读结果。
本文深入分析Gitaly源码中的函数选项模式(Functional Options Pattern),详细展示了如何通过闭包和可变参数实现灵活配置。以git-rev-list命令封装为例,讲解了从结构体定义、选项函数设计到具体实现的完整流程,展现了Go语言中优雅处理复杂参数和默认值的高级设计模式,对Go开发者构建可维护API提供了实用范例。
Gitaly is a Git RPC service for handling all the git calls made by GitLab
repo: gitaly: https://gitlab.com/gitlab-org/gitaly
git rev-list 是 Git 中非常重要和有用的命令,它存在许多选项以及子命令,Gitaly 中对这些选项和命令进行了封装。
首先定义了一个 config 结构体,包含不同的配置选项:
1 | type ObjectType string |
然后定义一个函数,该函数的参数是上述结构体,且是指针类型参数,这点非常重要,它使得这个函数能够直接修改入参 cfg:
1 | type RevlistOption func(cfg *revlistConfig) |
然后是一系列对 git-rev-list 参数进行修改的方法,每个 WithXXX 方法调用前面的匿名函数,并返回 RevlistOption 类型变量:
1 | func WithBlobLimit(limit int) RevlistOption { |
git-rev-list 方法定义如下:
1 | func Revlist( |
在 Revlist() 中最后一个参数 options ...RevlistOption,它表示是不定长变参列表具体参数数量交给调用者。
具体实现如下:
1 | func Revlist( |
那么如何调用这个方法呢?搜索 Gitaly 源码可以看到:
在 ListBlobs 中是这样用的:
1 | func (s *server) ListBlobs(req *gitalypb.ListBlobsRequest, stream gitalypb.BlobService_ListBlobsServer) error { |
ListCommits 中根据请求中不同的 case 分别向 revlistOptions 变量追加不同的配置选项:
1 | func (s *server) ListCommits( |
ListLFSPointers 中省略定义 RevlistOption 变量,直接利用 Revlist 方法中不定参数特性,添加不同的配置选项:
1 | func (s *server) ListLFSPointers(in *gitalypb.ListLFSPointersRequest, stream gitalypb.BlobService_ListLFSPointersServer) error { |
总结:
这种设计模式对于多配置,多参数的方法非常适合,虽然代码实现起来有点麻烦,但是可读性强,使用灵活。这种模式还有一些变体,后面有时间再记录一下。