该系列文章总结常见设计模式的概念、使用场景与Go的实现方案,,但实际上Go语言并不需要刻意地去过度使用设计模式,反而与Go大道至简地思想冲突。

本篇介绍建造者模式

建造者模式概念

建造者模式主要是应用于对一个类中有非常多的属性,而我们希望这些成员属性中有一些在构造过程中是可以选填的,因此导致构造器方法的入参组合会比较灵活多变。

而在 Java 中,可以通过方法重载的方式,实现同一个构造器方法对应多个不同的入参组合,可以在一定程度上缓解这个问题;Pyhon更是可以通过设置默认值灵活设置初始值。

但是在 Golang 中,每个方法的入参类型是确定的,也没有默认值这一说法。如果每一种初始函数都要定义不同的方法,那就会导致NewXXX的初始化方法过多,代码混乱。

对于这个场景,就可使用建造者模式,通过额外引入建造者的角色,使其可以链式调用的方式让用户能够灵活地完成成员属性的组装以及类实例的构造.

建造者模式的优势就是内聚了构造类实例的职责,为构造过程中成员属性的组合提高了更高的灵活度,从而降低了代码的冗余度.

以下为建造者模式的uml图演示: 例如有一个学生类,与学号、姓名、学院、年龄字段。而初始化时我想自由的去初始化对应属性,可能我不需要年龄

image.png 通过去初始化这样一个学生构造类,我们就可以比较灵活的去选择添加对应的属性去初始化

go
type Student struct{
    Age:    uint,
    Sno:    string,
    College:string,
    Name    string,
}


type StudentBuilder struct {
    Student
}


func NewStudentBuilder() *StudentBuilder {
    return &StudentBuilder{}
}

func (s *StudentBuilder) Name(name string) *StudentBuilder {
    f.Name = Name
    return s
}


func (s *StudentBuilder) Age(age string) *StudentBuilder {
    s.Age = age
    return s
}

func (s *StudentBuilder) Build() (*Student, error) {
    if s.name == "" {
        return nil, errors.New("miss name info")
    }
    if s.sno == "" {
        return nil, errors.New("miss sno info")
    }


    return &Student{
        Age:    s.Age,
        Name:   s.Name,
        Sno:    s.Sno,
        College:s.College,
    }, nil
}

这样我们就可以builder这一个类去自由选择字段去初始化了 stu,err:=NewStudentBuilder().Name("zhangsan").Build()

并且在build中我们还可以去定义必选的字段。

函数选项模式

但其实实际上golang中,我们平时针对这个问题使用的比较多的其实是函数选项模式,相信各位使用一些开源框架在初始化的时候,传参往往会语言withXXX()的参数,这其实就是用的函数选项模式去DIY传参

就拿刚刚那个例子举例:

go
type Student struct{
    Age:    uint,
    Sno:    string,
    College:string,
    Name    string,
}



type OptionFunc func(*Student)



// WithSno 将 Student 的 Sno 字段设置为指定值
func WithSno(sno int) OptionFunc {
	return func(s *Student) {
		s.Sno = sno
	}
}

// WithName 将 Student 的 Name 字段设置为指定值
func WithSno(name int) OptionFunc {
	return func(s *Student) {
		s.name = name
	}
}

像这样去定义,那我们在定义初始化函数时就可以将必须传入的属性作为参数传入,将可选的参数通过[]Options进行传入

go
func NewStudent(college string, opts ...OptionFunc) *Student {
	s := &Student{College: college}
	for _, opt := range opts {
		opt(s)
	}
	return s
}

s:=NewStudent("挖掘机学院",withName("张三"))