藁城手机网站建设,汉化版网站开发软件,动漫制作app,seo网站关键词优化前言
通过这篇文章《为什么说Go的函数是”一等公民“》#xff0c;我们了解到了什么是“一等公民”#xff0c;以及都具备哪些特性#xff0c;同时对函数的基本使用也更加深入。
本文重点介绍下Go设计模式之函数选项模式#xff0c;它得益于Go的函数是“一等公民”#…前言
通过这篇文章《为什么说Go的函数是”一等公民“》我们了解到了什么是“一等公民”以及都具备哪些特性同时对函数的基本使用也更加深入。
本文重点介绍下Go设计模式之函数选项模式它得益于Go的函数是“一等公民”很好的一个应用场景广泛被使用。
什么是函数选项模式
函数选项模式Functional Options Pattern 也称为选项模式Options Pattern是一种创造性的设计模式允许你使用接受零个或多个函数作为参数的可变构造函数来构建复杂结构。我们将这些函数称为选项由此得名函数选项模式。
看概念有点太生硬难懂了下面通过例子来讲解下怎么使用由浅入深通俗易懂。
怎么使用函数选项模式
一般水平
先来一个简单例子这个Animal结构体怎么构造出一个实例对象
type Animal struct {Name stringAge intHeight int
}通常的写法
func NewAnimal(name string, age int, height int) *Animal {return Animal{Name: name,Age: age,Height: height,}
}a1 : NewAnimal(小白兔, 5, 100)简单易懂结构体有哪些属性字段那么构造函数的参数就相应做定义并传入
带来的问题
代码耦合度高加属性字段构造函数就得相应做修改调用的地方全部都得改势必会影响现有代码代码灵活度低属性字段不能指定默认值每次都得明确传入
例如现计划新加3个字段Weight体重、CanRun是否会跑、LegNum几条腿同时要指定默认值CanRuntrue、LegNum4
新结构体定义
type Animal struct {Name stringAge intHeight intWeight intCanRun boolLegNum int
}代码实现函数加新参数定义但默认值貌似实现不了得调用构造函数时明确传入
func NewAnimal(name string, age int, height int, weight int, canRun bool, legNum int) *Animal {return Animal{Name: name,Age: age,Height: height,Weight: weight,CanRun: canRun,LegNum: legNum,}
}a1 : NewAnimal(小白兔, 5, 100, 120, true, 4)后续逐步加新字段这个构造函数就会被撑爆了如果调用的地方越多那么越伤筋动骨。
高阶水平
既然常规写法太low难以实现新需求那么我们就来玩点高阶的引出主题函数选项模式
首先需要先定义一个函数类型OptionFunc
type OptionFunc func(*Animal)然后根据新结构体字段定义With开头的函数返回函数类型为OptionFunc的闭包函数内部逻辑只需要实现更新对应字段值即可
func WithName(name string) OptionFunc {return func(a *Animal) { a.Name name }
}func WithAge(age int) OptionFunc {return func(a *Animal) { a.Age age }
}func WithHeight(height int) OptionFunc {return func(a *Animal) { a.Height height }
}func WithWeight(weight int) OptionFunc {return func(a *Animal) { a.Weight weight }
}func WithCanRun(canRun bool) OptionFunc {return func(a *Animal) { a.CanRun canRun }
}func WithLegNum(legNum int) OptionFunc {return func(a *Animal) { a.LegNum legNum }
}再然后优化构造函数的定义和实现name作为必传参数其他可选并且实现CanRun和LegNum两个字段指定默认值
func NewAnimal(name string, opts ...OptionFunc) *Animal {a : Animal{Name: name, CanRun: true, LegNum: 4}for _, opt : range opts {opt(a)}return a
}最后调用优化后的构造函数快速实现实例的初始化。想要指定哪个字段值那就调用相应的With开头的函数完全做到可配置化、可插拔不指定还支持了默认值
a2 : NewAnimal(大黄狗, WithAge(10), WithHeight(120))
fmt.Println(a2)
a3 : NewAnimal(大灰狼, WithHeight(200))
fmt.Println(a3)输出结果
{大黄狗 10 120 0 true 4}
{大灰狼 0 200 0 true 4}带来的好处
高度的可配置化、可插拔还支持默认值设定很容易维护和扩展容易上手大幅降低新来的人试错成本
开源项目中的实践案例
函数选项模式不单单是我们业务代码中有使用现在大量的标准库和第三库都在使用。
下面带着大家一块来看看apollo配置中心客户端第三库shima-park/agollo看看它是怎么玩的怎么做配置初始化
核心代码
type Options struct {AppID string // appidCluster string // 默认的集群名称默认defaultDefaultNamespace string // Get时默认使用的命名空间如果设置了该值而不在PreloadNamespaces中默认也会加入初始化逻辑中PreloadNamespaces []string // 预加载命名空间默认为空ApolloClient ApolloClient // apollo HTTP api实现Logger Logger // 日志实现类可以设置自定义实现或者通过NewLogger()创建并设置有效的io.Writer默认: ioutil.DiscardAutoFetchOnCacheMiss bool // 自动获取非预设以外的Namespace的配置默认falseLongPollerInterval time.Duration // 轮训间隔时间默认1sBackupFile string // 备份文件存放地址默认.agolloFailTolerantOnBackupExists bool // 服务器连接失败时允许读取备份默认falseBalancer Balancer // ConfigServer负载均衡EnableSLB bool // 启用ConfigServer负载均衡RefreshIntervalInSecond time.Duration // ConfigServer刷新间隔ClientOptions []ApolloClientOption // 设置apollo HTTP api的配置项EnableHeartBeat bool // 是否允许兜底检查默认falseHeartBeatInterval time.Duration // 兜底检查间隔时间默认300s
}func newOptions(configServerURL, appID string, opts ...Option) (Options, error) {var options Options{AppID: appID,Cluster: defaultCluster,ApolloClient: NewApolloClient(),Logger: NewLogger(),AutoFetchOnCacheMiss: defaultAutoFetchOnCacheMiss,LongPollerInterval: defaultLongPollInterval,BackupFile: defaultBackupFile,FailTolerantOnBackupExists: defaultFailTolerantOnBackupExists,EnableSLB: defaultEnableSLB,EnableHeartBeat: defaultEnableHeartBeat,HeartBeatInterval: defaultHeartBeatInterval,}for _, opt : range opts {opt(options)}//...省略return options, nil
}type Option func(*Options)//一系列函数作为选项
func PreloadNamespaces(namespaces ...string) Option {return func(o *Options) {o.PreloadNamespaces append(o.PreloadNamespaces, namespaces...)}
}
func AutoFetchOnCacheMiss() Option {return func(o *Options) {o.AutoFetchOnCacheMiss true}
}
//...玩法
使用Options结构体定义出apollo需要使用到的所有配置字段定义一系列函数作为选项对配置字段做初始化设置例如设置容灾文件路径、预加载的namespace、轮训间隔时间等等构造函数里初始化一个Options的实例对象并且根据传入的函数选项进行配置字段的更新最终返回这个实例对象获取到实例对象调用相应的方法做相应的操作。
总结
由浅入深的讲解了下实例对象初始化一般写法和高阶写法。用好这个高阶写法函数选项模式让代码更高比格。还不会使用的Gopher赶紧学起来用起来。
文章首发
我的文章会首发在我的公众号程序员升职加薪之旅欢迎大家关注第一时间收到最新内容。
一起学习
我的所有文章都会首发在我的 学习小圈子 欢迎加入我们一起学习进步一起升职加薪。