Golang Functional Options 來了解一下?

在開發(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ā)布!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容