Options模式
推荐阅读大佬原文一些实用的 Go 编程模式 | Options模式
此记录仅做个人笔记,设计模式适用于各个语言
一、Options模式解决什么问题
Options模式可以让具有多个可选参数的函数或方法更整洁和好扩展,当一个函数具有6个以上的可选参数使用这种模式有很明显的优化体验。
二、多参数函数传值解决方案
对需要多个参数的函数传参方式有以下几种解决方式:
- 传入固定参数值
- 传入配置对象
- 传入可变参数
- 使用options模式
2.1 传入固定参数值
type Person struct{
Id string
Name string
Position string
}
type Company struct {
Name string
}
//
// Dimission
// @Description: 员工离职处理
// @param dP 离职员工
// @param mP 主管
// @param tP 接手人
// @param company 公司
// @param wData 工作资料
// @param dFile 离职文件
// @param wList 交接清单
// @param dTime 离职时间
// @return proof 离职证明
//
func Dimission(dP, mP,tP Person,company Company, wData []byte, dFile []byte, wList map[string]string, dTime time.Time) (proof []byte) {
// 第一步就是检查各个参数是否有效,某些参数无效时填入默认值,例如离职时间
if wData !=nil {
// ...
}
if dFile !=nil {
// ...
}
if wList != nil {
// ...
}
return nil
}
优点:简单直接
缺点:
-
函数逻辑中必须全部参数的判空逻辑等无关代码
-
对于用户不需要自定义的值,也需要调用者传入零值
python中有默认值来解决
java中可以通过重载来解决
go中需要通过重新声明新的方法,然后再新方法中自动设置旧方法的默认值,来进行解决
2.2 传入配置对象 (常用)
type DimissionJob struct {
DP Person
MP Person
TP Person
Company
WData []byte
DFile []byte
WList []byte
DTime time.Time
}
func DimissionWithStruct(dj *DimissionJob) (proof []byte) {
// 检查各个参数
return nil
}
优点:代码简洁,面向对象
缺点:线程不安全
go中是值传递的,为了避免复制参数,提高性能,这里参数接收的是指针,有可能在函数内部进行时,对象在外部被更改。
日常使用时,传递都是对象的
func DimissionWithStruct(dj DimissionJob)
,工作中常常这么写
2.3 传入可变参数
func DimissionWithVariable(dP Person, opts ...interface{}) (proof []byte) {
// 检查各个参数
return nil
}
优点:参数可变
缺点:需要按设置好顺序传参,否则无法正确变化
2.4 使用options模式
type DimissionOption struct {
DP Person
MP Person
TP Person
Company
WData []byte
DFile []byte
WList []byte
DTime time.Time
}
type Option struct {
apply func(option *DimissionOption)
}
func defaultDimissionOptions() *DimissionOption {
return &DimissionOption{
// 默认请求选项
// ....
}
}
func WithWData(d []byte) *Option {
return &Option{
apply: func(option *DimissionOption) {
option.WData = d
},
}
}
func WithDFile(d []byte) *Option {
return &Option{
apply: func(option *DimissionOption) {
option.DFile = d
},
}
}
func WithDTime(d time.Time) *Option {
return &Option{
apply: func(option *DimissionOption) {
option.DTime = d
},
}
}
func DimissionWithVariable(dP, mP,tP Person,company Company, options ...*Option) (proof []byte) {
reqOpts := defaultDimissionOptions() // 默认的请求选项
for _, opt := range options { // 在reqOpts上应用通过options设置的选项
opt.apply(reqOpts)
}
// 进行业务操作
return nil
}
func Test() {
dP := Person{Name: "职员A"}
mP := Person{Name: "主管B"}
tP := Person{Name: "职员C"}
c := Company{Name: "XXXX科技公司"}
wData := []byte("工作资料")
dFile := []byte("交接资料")
dTime, err := time.Parse("2002-01-02", "2021-10-27")
if err != nil {
log.Fatal(err.Error())
return
}
// 使用
DimissionWithVariable(dP, mP,tP, c, WithWData(wData))
DimissionWithVariable(dP, mP,tP, c, WithWData(wData), WithDFile(dFile), WithDTime(dTime))
}
如果后面要给配置对象里增加其他配置项,只需要扩充类型的字段,在定义一个对应的With方法即可,扩展性完全在可接受范围内。
个人总结:
没有最好的解决方案,在选择方案时需要在项目开发与项目拓展两端进行权重,然后选择适合的解决方案。这里的options模式在rpc框架得到了广泛的使用,说明了options模式也是成熟的解决方式