在開發(fā)或者學(xué)習(xí)源碼過程中,不少人應(yīng)該見過下面的代碼片段
type Option func(opts *Options)
// 新建一個server,client,pool等
func NewXXX(name string, opts ...Option) {}
剛開始從java轉(zhuǎn)go的時候,我看這些代碼真的是看得一臉懵逼,看不懂這些option是什么,也不明白為什么需要它們的存在
直到后來,我才知道,這叫函數(shù)式選項(Functional Options)
函數(shù)式選項是Golang中實現(xiàn)簡潔API的一種方式
在使用NewXXX函數(shù)構(gòu)建struct的時候,struct中的屬性并不都是必須的,這些非必須屬性,在構(gòu)建struct的過程中可以通過函數(shù)式選項的方式,實現(xiàn)更加簡潔的API
假設(shè)需要實現(xiàn)一個協(xié)程池GPool,其中必備的屬性有協(xié)程數(shù)量size,還有可選項:是否異步async,錯誤處理errorHandler,最大緩存任務(wù)數(shù)maxTaskNums,那么struct的設(shè)計應(yīng)該如下
package pool
// Option 定義函數(shù)式選項
type Option func(options *Options)
// GPool 協(xié)程池
type GPool struct {
size int64 // 協(xié)程數(shù)量
options *Options
}
type ErrorHandler func(err error)
// Options 將非必須的選項都放到這里
type Options struct {
async bool // 是否支持異步提交任務(wù)
handler ErrorHandler // 任務(wù)執(zhí)行出錯時,回調(diào)該函數(shù)
maxTasks int64 // 協(xié)程池所接受的最大緩存任務(wù)數(shù)
}
// NewGPool 新建協(xié)程池
func NewGPool(size int64, opts ...Option) *GPool {
options := loadOpts(opts)
return &GPool{
size: size,
options: options,
}
}
func loadOpts(opts []Option) *Options {
options := &Options{}
for _, opt := range opts {
opt(options)
}
return options
}
func WithAsync(async bool) Option {
return func(options *Options) {
options.async = async
}
}
func WithErrorHandler(handler ErrorHandler) Option {
return func(options *Options) {
options.handler = handler
}
}
func WithMaxTasks(maxTasks int64) Option {
return func(options *Options) {
options.maxTasks = maxTasks
}
}
如果需要創(chuàng)建一個協(xié)程池,協(xié)程數(shù)量為100,只需要這樣寫
p := pool.NewGPool(100)
如果需要創(chuàng)建一個協(xié)程池,協(xié)程數(shù)量為100并支持異步提交,只需要這樣寫
p := pool.NewGPool(100, pool.WithAsync(true))
如果需要穿件一個協(xié)程池,協(xié)程數(shù)量為100、支持異步提交,并且回調(diào)自定義錯誤處理,只需要這樣寫
p := pool.NewGPool(100,
pool.WithAsync(true),
pool.WithErrorHandler(func(err error) {
// 處理任務(wù)執(zhí)行過程中發(fā)生的error
}),
)
這樣的寫法是不是感覺更加簡潔?
如果不使用函數(shù)式選項,我們還可以怎么做?
第一種,直接構(gòu)建struct,但是需要填寫非常非常多的屬性,對調(diào)用者并不友好
func NewGPool(size int64, async bool, handler ErrorHandler, maxTasks int64) *GPool {
return &GPool{
size: size,
options: &Options{
async: async,
handler: handler,
maxTasks: maxTasks,
},
}
}
當(dāng)struct中的屬性變得越來越多時候,這長長的函數(shù)簽名,對于調(diào)用者而言,簡直是噩夢般的存在
第二種,使用建造者模式
func (builder *GPoolBuilder) Builder(size int64) *GPoolBuilder {
return &GPoolBuilder{p: &GPool{
size: size,
options: &Options{},
}}
}
func (builder *GPoolBuilder) WithAsync(async bool) *GPoolBuilder {
builder.p.options.async = async
return builder
}
func (builder *GPoolBuilder) Build() *GPool {
return builder.p
}
調(diào)用者使用經(jīng)構(gòu)建者模式封裝后的API,還是非常舒服的
builder := GPoolBuilder{}
p := builder.Builder(100).WithAsync(true).Build()
但是,卻要額外維護一份屬于builder的代碼,雖然使用簡潔,但是具備一定的維護成本!!
總的來看,函數(shù)式選項還是最佳的選擇方案,開發(fā)者通過它能夠構(gòu)建簡潔,友好的API。
本文由博客一文多發(fā)平臺 OpenWrite 發(fā)布!