在开发或者学习源码过程中,不少人应该见过上面的代码片段

type Option func(opts *Options)// 新建一个server,client,pool等func NewXXX(name string, opts ...Option) {}

刚开始从javago的时候,我看这些代码真的是看得一脸懵逼,看不懂这些option是什么,也不明确为什么须要它们的存在

直到起初,我才晓得,这叫函数式选项(Functional Options)

函数式选项是Golang中实现简洁API的一种形式

在应用NewXXX函数构建struct的时候,struct中的属性并不都是必须的,这些非必须属性,在构建struct的过程中能够通过函数式选项的形式,实现更加简洁的API

假如须要实现一个协程池GPool,其中必备的属性有协程数量size,还有可选项:是否异步async,错误处理errorHandler,最大缓存工作数maxTaskNums,那么struct的设计应该如下

package pool// Option 定义函数式选项type Option func(options *Options)// GPool 协程池type GPool struct {    size    int64 // 协程数量    options *Options}type ErrorHandler func(err error)// Options 将非必须的选项都放到这里type Options struct {    async    bool         // 是否反对异步提交工作    handler  ErrorHandler // 工作执行出错时,回调该函数    maxTasks int64        // 协程池所承受的最大缓存工作数}// NewGPool 新建协程池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    }}

如果须要创立一个协程池,协程数量为100,只须要这样写

p := pool.NewGPool(100)

如果须要创立一个协程池,协程数量为100并反对异步提交,只须要这样写

p := pool.NewGPool(100, pool.WithAsync(true))

如果须要穿件一个协程池,协程数量为100、反对异步提交,并且回调自定义错误处理,只须要这样写

p := pool.NewGPool(100,    pool.WithAsync(true),    pool.WithErrorHandler(func(err error) {        // 解决工作执行过程中产生的error    }),)

这样的写法是不是感觉更加简洁?

如果不应用函数式选项,咱们还能够怎么做?

第一种,间接构建struct,然而须要填写十分十分多的属性,对调用者并不敌对

func NewGPool(size int64, async bool, handler ErrorHandler, maxTasks int64) *GPool {    return &GPool{        size:    size,        options: &Options{            async:    async,            handler:  handler,            maxTasks: maxTasks,        },    }}

struct中的属性变得越来越多时候,这长长的函数签名,对于调用者而言,几乎是噩梦般的存在

第二种,应用建造者模式

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}

调用者应用经构建者模式封装后的API,还是十分难受的

    builder := GPoolBuilder{}    p := builder.Builder(100).WithAsync(true).Build()

然而,却要额定保护一份属于builder的代码,尽管应用简洁,然而具备肯定的保护老本!!

总的来看,函数式选项还是最佳的抉择计划,开发者通过它可能构建简洁,敌对的API。

本文由博客一文多发平台 OpenWrite 公布!