阅读了文章 Functional Options in Go: Implementing the Options Pattern in Golang 之后,感觉内容很实在,推荐大家阅读原文。
假设我们需要提供一个获取 “笔记信息” 的接口,它包括内容、阅读数、作者、创建时间、标签、分类等等信息。但并不是所有的调用方都需要全量的笔记信息,有的调用方可能只需要笔记的作者信息就可以。一般来说,我们会如何处理这种情况呢?如果我们要新返回一个元数据信息,比如是否私密(Private),又该如何处理呢?
函数参数方式
type NoteClient struct {
}
// func1 通过明确的参数来控制加载的数据
func (cli *NoteClient) GetV1(ctx context.Context,
withContent, withView, vithAuthor, withCreateTime bool) (*NoteInfo, error) {
}
// func2 通过扩展的参数来控制
func (cli *NoteClient) GetV2(ctx context.Context, withOptions ...bool) (*NoteInfo, error) {
}
GetV1 的扩展性很差,如果这个接口在项目中使用的地方特别多,要新加一个 withPrivate 的参数,之前所有调用 GetV1 的方法都需要加这个参数,扩展性非常差。
GetV2 是 GetV1 的另一种写法,并没有带来任何优势,甚至还引入了一个劣势:不能一眼看出方法提供了哪些选项。
扩展结构体字段
type NoteClient struct {
WithContent bool
WithView bool
WithAuthor bool
}
// 或者单独声明一个参数结构体
type NoteOption struct {
WithContent bool
WithView bool
WithAuthor bool
}
// func1 在结构体中加扩展字段
func (cli *NoteClient) GetV1(ctx context.Context) (*NoteInfo, error) {
}
// func2 通过一个结构体 withOption 参数来控制
func (cli *NoteClinet) GetV2(ctx context.Context, withOption *NoteOption) (*NoteInfo, error) {
}
如果要扩展是否私密,只需要在结构体中追加新的字段 WithPrivate
定义函数选项
type NoteClient struct {
WithContent bool
WithView bool
WithAuthor bool
}
type NoteOptionFunc func(*NoteClient)
func WithContent() NoteOptionFunc {
return func(opt *NoteClient) {
opt.WithContent = true
}
}
func WithNoContent() NoteOptionFunc {
return func(opt *NoteClient) {
opt.WithContent = false
}
}
func NewNoteClient(opts ...NoteOptionFunc) *NoteClient {
cli := &NoteClient{}
for _, f := range opts {
f(cli)
}
return cli
}
很多人都使用这种模式,其实也没啥问题,就是多写了很多代码,别人一行代码,我们可能需要5行。