跳至主要內容

建造者模式

离心原创大约 3 分钟tutorialgolangdesign-patterns

建造者模式是一种创建型设计模式,它可以将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

建造者模式

定义

建造者模式的主要作用有:

封装一个复杂对象的创建过程。客户端不需要知道复杂对象的内部结构,只需要指定需要的属性就可以构建一个复杂对象。

可以更精细地控制对象的创建过程。可以通过不同的建造者创建不同的产品对象。

将复杂对象的创建代码与业务逻辑代码分离,提高复用性和灵活性。

用处

Go语言中实现建造者模式的主要步骤:

定义一个Builder接口,指定需要实现的方法,如BuildPartA()、BuildPartB()等。

实现Builder接口,创建具体的建造者,实现接口中的方法以构建产品的各个部件。

定义一个Director结构体,在其中定义一个方法,该方法会使用Builder接口构建完整的产品对象。

Director通过Builder接口与不同的具体建造者交互,以构建完整的产品对象。

建造者模式的主要优点是:

  • 封装复杂对象的创建过程
  • 分离复杂对象的创建和表示
  • 实现创建过程与表示分离

建造者模式的主要缺点:

  • 建造者模式比直接创建复杂对象更加复杂
  • 建造者与Director之间存在紧密的依赖

相比建造者模式,Go语言更推荐使用函数选项模式。因为函数选项模式可以实现类似的功能,但是语法更简单,不需要引入多余的接口和类型,更符合Go语言的简洁风格。函数选项模式可以通过闭包和高阶函数实现,语法轻量且易于使用。

所以在Go语言中实现复杂对象的创建,更推荐使用函数选项模式,而不是较重且复杂的建造者模式。函数选项模式可以实现建造者模式的优点,同时代码更简洁。

实现

package builder

type Gender int

const (
	Men Gender = iota
	Women
)

type Person struct {
	Name   string
	Age    int
	Gender Gender
	Phone  int
}

type PersonConfig struct {
	Age    int
	Gender Gender
	Phone  int
}

const (
	defaultAge    = 20
	defaultGender = Men
	defaultPhone  = 12306
)

func NewPerson(name string, config *PersonConfig) *Person {
	p := &Person{
		name,
		defaultAge,
		defaultGender,
		defaultPhone,
	}

	if config.Age != defaultAge {
		p.Age = config.Age
	}

	if config.Phone != defaultPhone {
		p.Phone = config.Phone
	}

	if config.Gender != defaultGender {
		p.Gender = config.Gender
	}

	return p
}

func (p *Person) SetAge(age int) {
	p.Age = age
}

func (p *Person) SetName(name string) {
	p.Name = name
}

func (p *Person) SetPhone(phone int) {
	p.Phone = phone
}

func (p *Person) SetGender(Gender Gender) {
	p.Gender = Gender
}

另一种实现

但其实Go语言中并不推荐传统的建造者模式,更推荐函数选项模式。


type OptionFunc func(*Person)

func WithPhone(phone int) OptionFunc {
	return func(p *Person) {
		p.Phone = phone
	}
}

func WithGender(gender Gender) OptionFunc {
	return func(p *Person) {
		p.Gender = gender
	}
}

func WithAge(age int) OptionFunc {
	return func(p *Person) {
		p.Age = age
	}
}

func NewPerson2(name string, optionFunc ...OptionFunc) *Person {
	p := &Person{
		Name: name,
	}

	for _, o := range optionFunc {
		o(p)
	}

	return p
}

总结

使用工厂模式的一些优点是降低耦合度、可扩展性和灵活性。缺点是多了一层抽象层,导致代码增多,实现复杂。

总的来说,当需要将对象创建代码与消费者代码分离,以及需要动态添加新的派生产品类时,工厂模式是非常有用的。它非常适合需要运行时配置灵活性和多态性的场景。