背景
之前看过用go实现的23种常见的设计模式,但是有些模式总感觉很别扭,像是为了实现而实现。
此外,go也不是个强面向对象语言,有些依赖于继承的设计模式用go实现起来就更加怪异了。难道go就没有“方言化”的设计模式吗?
非也,库用多了,你会发现很多源码里面也用到了设计模式,而且非常优雅,像是很多需要New一个client的源码就会用到Options
模式, 而像gorm库,用到的链式查询,是用了builder
模式,还有gin中的中间件,用到了责任链模式...
果然阅读源码是快速进步的必经之路呀
options 模式
大家知道,go没有构造函数这么一说,所以返回一个实例完全靠通过传入的形参来初始化结构体的字段,但是假如结构体字段一多起来,函数要传入的形参长到换三行都放不下,更别提有时候就传一两个,要是排列组合起来,1000行代码900行New
type Student struct {
Name string
Class int
Number int
TeacherName string
Address string
Age int
}
func NewStudentClassInfo(class int, teacher string) *Student {
return &Student{
TeacherName: teacher,
Class: class,
}
}
func NewStudentInfo(address string, name string, number int, age int) *Student {
return &Student{
Name: name,
Address: address,
Number: number,
Age: age,
}
}
//....
so, 这时候就需要安利你 Functional Options 模式了
func main() {
s := NewStudent(
WithClass(1),
WithName("yezi"),
)
fmt.Println(s)
}
type Student struct {
Name string
Class int
Number int
TeacherName string
Address string
Age int
}
type OptionFun func(s *Student)
func NewStudent(opt ...OptionFun) *Student {
student := &Student{
Name: "undefined",
Class: 1,
}
for _, o := range opt {
o(student)
}
return student
}
// 每个参数的设置使用一个闭包函数完成,2^n - 1 -> n
func WithName(name string) OptionFun {
// 采用闭包的方式设置
return func(s *Student) {
s.Name = name
}
}
func WithClass(class int) OptionFun {
return func(s *Student) {
s.Class = class
}
}
本质就是让每一个配置项的设置都独立成为一个函数,而且是可变式的,在New函数中先赋默认值,然后通过遍历执行可变参数函数来设置对应的字段值即可
- 个数无关
- 参数位置无关
- 解耦
另一种写法(源码中常见的,继续封装了一层并抽象化)
type Student struct {
Name string
Class int
Number int
TeacherName string
Address string
Age int
}
type Option interface {
Apply(s *Student)
}
type OptionFun func(s *Student)
func (f OptionFun) Apply(s *Student) {
f(s)
}
func NewStudent(opt ...Option) *Student {
student := &Student{
Name: "undefined",
Class: 1,
}
for _, o := range opt {
o.Apply(student)
}
return student
}
// 每个参数的设置使用一个闭包函数完成,2^n - 1 -> n
func WithName(name string) OptionFun {
// 采用闭包的方式设置
return OptionFun(func(s *Student) {
s.Name = name
})
}
func WithClass(class int) OptionFun {
return OptionFun(func(s *Student) {
s.Class = class
})
}