Functional Options Pattern(函数式选项模式)可用于传递不同选项配置到方法中,而且每次新增选项时,可以不改变接口保持兼容。还可以用来实现类似 Java/C++ 中方法重写的功能~
Go 中结构体没有构造方法,所以有的时候我们会自己提供一个 New
方法。考虑你有某个客户端结构体:
type Client struct {
// ...
}
func New() *Client {
return &Client{}
}
有一天需要新增一个 Timeout 参数,由于 Go 不支持方法重载,因此我们不得不修改 New
方法传入一个 Timeout 参数, 但是一旦修改了 New
的签名,将会造成不兼容。于是我们只好新增一个工厂方法 NewClientWithTimeout
并且修改旧有的 New
方法,设置它的 Timeout 为一个默认值。
但是下次再需要新增字段,还是很麻烦……
我们可以使用可变参数来解决这一问题。
先定义一个配置结构体:
type Config struct { Timeout time.Duration Cluster string } type Option func(*Config)
然后对每个配置项提供一个配置函数
func WithTimeout(timeout time.Duration) Option {
return func(config *Config) {
config.Timeout = timeout
}
}
func WithCluster(cluster string) Option {
return func(config *Config) {
config.Cluster = cluster
}
}
最后在 New
方法上使用可变参数来接收配置函数
func New(opts ...Option) *Client {
// default config
config := &Config{Timeout: 20 * time.Millisecond, Cluster: "default"}
// for each custom options
for _, opt := range opts {
opt(config)
}
return &Client{
Timeout: config.Timeout,
Cluster: config.Cluster,
}
}
下次再需要新增配置项,只需要新增 WithXxx
配置函数,修改默认 Config
的值即可。调用方可以直接升级版本,如果不传入新的配置函数,则会使用默认配置。
New()
New(WithTimeout(10 * time.Millisecond))
New(WithTimeout(10*time.Millisecond), WithCluster("my-cluster"))
这个模式还可以用来模仿 Java8 的接口默认实现、或者用来模仿 Java 中的子类重写(override)父类方法。
package foo
// 暴露的接口
type MyInterface interface {
SayHi() string
}
// 实现类
type base struct {
// 得益于 Go 中函数是一等公民,可以将函数存在字段中
sayHi func() string
}
// 使用函数字段实现接口
func (b *base) SayHi() string {
return b.sayHi()
}
// 提供自定义能力
type Option func(*base)
func SayHi(f func() string) Option {
return func(b *base) {
b.sayHi = f
}
}
// 默认行为
func newDefault() *base {
return &base{
sayHi: func() string {
return "Hello, World"
},
}
}
// 构造方法
func New(opts ...Option) MyInterface {
b := newDefault()
for _, opt := range opts {
opt(b)
}
return b
}
使用方:
func ExampleNew() {
fmt.Println(foo.New().SayHi())
fmt.Println(foo.New(foo.SayHi(func() string {
return "你好,世界"
})).SayHi())
// Output:
// Hello, World
// 你好,世界
}
这样就可以通过传入函数的形式改变默认的行为,看起来像重写😂
《Go Streams 仿照 Java8 的流造的轮子》中 stage
接口使用了这一模式。
声明
- 本作品采用署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。除非特别注明, 霖博客文章均为原创。
- 转载请保留本文(《Go 中的 Options 模式》)链接地址: https://youthlin.com/?p=1762
- 订阅本站:https://youthlin.com/feed/
“Go 中的 Options 模式”上的1条回复
[…] Go 中的 Options 模式 […]