实现功能选项模式
在日常Go编程中,我们经常会实现一些带有设置选项的创建型函数。对于一些复杂的Go包中的创建型函数,它要提供的可设置选项有时多达数十种,甚至后续还会增加。那么在选项增长的同时,依旧保持对外接口的稳定性,就成了关键。
例:实现一个NewFinishedHouse函数,其中选项有:
- 装修风格
- 是否是中央空调
- 地面材料
- 墙面材料
还有可能会增加的选项。
版本1实现:使用结构体封装配置选项
type FinishedHouse struct {
style string
isCentral bool
floor string
wall string
}
type Option struct {
Style string
IsCentral bool
Floor string
Wall string
}
func NewFinishedHouse(option *Option) *FinishedHouse {
var (
style = "fashion"
isCentral = true
floor = "normal"
wall = "standard"
)
if option != nil {
style = option.Style
isCentral = option.IsCentral
floor = option.Floor
wall = option.Wall
}
return &FinishedHouse{
style: style,
isCentral: isCentral,
floor: floor,
wall: wall,
}
}
采用这种方式的好处是,当选项增加时,只需增加结构体option的字段,原本的函数签名是不变的。
版本2实现:使用功能选项模式
Go语言之父Rob Pike早在2014年就在其博文“自引用函数与选项设计”[2]中论述了一种被后人称为“功能选项”(functional option)的模式,这种模式应该是目前进行功能选项设计的最佳实践。
type FinishedHouse struct {
style string
isCentral bool
floor string
wall string
}
type Option func(*FinishedHouse)
func NewFinishedHouse(options ...Option) *FinishedHouse {
h := &FinishedHouse{
style: "fashion",
isCentral: true,
floor: "normal",
wall: "standard",
}
for _, option := range options {
option(h)
}
return h
}
func WithStyle(style string) Option {
return func(f *FinishedHouse) {
f.style = style
}
}
func WithIsCentral(isCentral bool) Option {
return func(f *FinishedHouse) {
f.isCentral = isCentral
}
}
func WithFloor(floor string) Option {
return func(f *FinishedHouse) {
f.floor = floor
}
}
func WithWall(wall string) Option {
return func(f *FinishedHouse) {
f.wall = wall
}
}
func main() {
fmt.Printf("%+v", NewFinishedHouse()) // 默认选项
fmt.Printf("%+v", NewFinishedHouse(
WithStyle("classic"),
WithIsCentral(false),
))
}
我们看到在该方案中,FinishedHouse的配置选项不是通过存储在结构体中的配置参数传入的,而是通过对FinishedHouse值本身进行操作的函数调用(利用函数的“一等公民”特质)实现的,并且通过使用变长参数函数,我们可以随意扩展传入的配置选项的个数。
评论