Go语言使用选项模式设置函数的默认参数
Python与Go使用默认参数对比
在使用Go语言写函数时,不能像Python一样设置函数的默认参数,有时候,不能设置默认参数让我们处理起来有点麻烦,特别是有时候函数的参数想让使用者自己决定的时候,不禁让人感叹Python的方便。
比如,我们实现一个对象——点奶茶:
使用Python实现
class Tea:
def __init__(self , name , heat=False , sugar = 5):
self.name = name
self.heat = heat
self.sugar = sugar
def get_tea(self):
print("name:{}, heat:{}, sugar:{},".format(self.name , self.heat ,self.sugar))
t = Tea("hahahha")
t.get_tea()
t = Tea("tea2",True,3)
t.get_tea()
使用Go语言实现
package main
import (
"fmt"
)
// 定义默认参数的常量
const (
defaultHeat = false
defaultSugar = 5
)
type Tea struct {
name string
heat bool
sugar int
}
// 默认参数的选项,中间对象
type TeaOptions struct {
heat bool
sugar int
}
// 创建默认参数对象
func NewDaulft() *TeaOptions{
return &TeaOptions{
heat : defaultHeat,
sugar: defaultSugar,
}
}
// 创建具体对象
func NewTea(name string , ops *TeaOptions)( *Tea,error){
return &Tea{
name: name,
heat: ops.heat,
sugar: ops.sugar,
},nil
}
func (t Tea) getTea() {
fmt.Printf("name:%s , heat:%t , sugar:%d\n", t.name, t.heat, t.sugar)
}
func main() {
// 使用默认参数
tea1, _ := NewTea("tea1", NewDaulft())
tea1.getTea()
// 不使用默认参数,创建TeaOptions对象传参
tea2, _ := NewTea("tea2", &TeaOptions{heat: true, sugar: 10})
tea2.getTea()
}
从上面可以看到,Go语言实现函数默认方式比较麻烦,而且,每次创建对象时都需要再创建一个中间对象——TeaOptions负责有默认参数的部分,操作起来很麻烦,并且发生变化就得修改很多处地方。
Go使用选项模式设置默认参数
为了解决这个麻烦,便于扩展,可以使用设计模式中的选项模式来创建对象。代码如下:
package main
import (
"fmt"
)
// 定义默认的常量
const (
defaultHeat = false
defaultSugar = 5
)
type Tea struct {
name string
heat bool
sugar int
}
// 在原有的基础上修改
// 默认参数的选项
type options struct {
heat bool
sugar int
}
// 接口类型,apply方法
type OptionContainer interface {
apply(*options)
}
// 函数类型,实现了apply方法 -- OptionContainer
type optionFunc func(*options)
func (f optionFunc) apply(o *options) {
f(o)
}
// 设置heat参数
func WithHeat(h bool) OptionContainer {
return optionFunc(func(o *options) { //optionFunc()为类型转换
o.heat = h
})
}
func WithSugar(s int) OptionContainer {
return optionFunc(func(o *options) { //optionFunc()为类型转换
o.sugar = s
})
}
func NewTea(name string, ops ...OptionContainer) (*Tea, error) {
// 填充默认值
opt := options{
heat: defaultHeat,
sugar: defaultSugar,
}
// 遍历可变参数,完成值的填充
for _, o := range ops {
o.apply(&opt)
}
return &Tea{
name: name,
heat: opt.heat,
sugar: opt.sugar,
}, nil
}
func (t Tea) getTea() {
fmt.Printf("name:%s , heat:%t , sugar:%d分\n", t.name, t.heat, t.sugar)
}
func main() {
// 使用默认参数
tea1, _ := NewTea("tea1")
tea1.getTea()
// 不使用默认参数
h2 := WithHeat(true)
tea2, _ := NewTea("tea2", h2, WithSugar(1))
tea2.getTea()
}
上面代码实现默认参数支持就比较贴切,只需要在创建的函数参数中设置好参数,不然就使用默认参数。当然,相应的,增加了许多代码,看起来更加复杂。不过,这些都是为了方便扩展。
总结
使用Go语言实现函数默认参数比较麻烦,可以采用中间对象的方法设置默认参数,或者使用选项模式传入一个能自己填充值的选项参数完成默认参数的设置。后者相对来说代码量会增多,更加复杂。但是,也带来了很多优点:
- 没有参数数量和顺序的限制,方便后续扩展。
- 在参数发生变化时保持兼容性
- 参数较多的情况下,可以选择性设置
注意,参数较少时就不要采用选项模式。
参考
项目参考:https://github.com/marmotedu
(@colin)