设计模式-06-建造者模式

建造者模式 该模式允许你使用相同的创建代码生成不同类型和形式的对象。将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

代码路径:

逻辑结构

建造者模式又可以称为生成器模式,主要分为四个部分

  • Builder:抽象建造者 (例子中类似 汽车图纸)
  • ConcreteBuilder:具体建造者(例子中是BYD/Tesla的造车工厂)
  • Director:指挥者 (例子中 类似一个高级工程师,根据不同的图纸对应流程生产不同的汽车)
  • Product:产品角色(例子中是车)
Builder

代码实现

代码路径:https://github.com/XBoom/DesignPatterns/tree/main/05_Builder

  1. 创建一个产品类,并定义其中所需的属性。

    1
    2
    3
    4
    5
    6
    // Car 定义一个产品 汽车
    type Car struct {
    battery string //电池
    electricMotor string //电机
    electronicControl string //电控
    }
  2. 创建一个抽象建造者接口,并定义其中需要实现的方法

    1
    2
    3
    4
    5
    6
    7
    // Builder 抽象即按照这接口,定义其需要实现的方法
    type Builder interface {
    BuildBattery()
    BuildElectricMotor()
    BuildElectronicControl()
    GetCar() *Car
    }
  3. 创建具体的建造者结构体,并实现抽象建造者接口中的方法,这里构建了两个类似的对象

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    // BYD 具体建造者
    type BYD struct {
    car Car
    }

    func (b *BYD) BuildBattery() {
    b.car.battery = "BYD Battery"
    }

    func (b *BYD) BuildElectricMotor() {
    b.car.electricMotor = "BYD Electric Motor"
    }

    func (b *BYD) BuildElectronicControl() {
    b.car.electronicControl = "BYD Electronic Control"
    }

    func (b *BYD) GetCar() *Car {
    return &b.car
    }

    // 具体构建者实现抽象接口
    var _ Builder = (*BYD)(nil)

    另外一个建造者

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    // Tesla 具体建造者
    type Tesla struct {
    car Car
    }

    func (t *Tesla) BuildBattery() {
    t.car.electronicControl = "Tesla Battery"
    }

    func (t *Tesla) BuildElectricMotor() {
    t.car.electronicControl = "Tesla Electronic Motor"
    }

    func (t *Tesla) BuildElectronicControl() {
    t.car.electronicControl = "Tesla Electronic Control"
    }

    func (t *Tesla) GetCar() *Car {
    return &t.car
    }

    var _ Builder = (*Tesla)(nil)
  4. 创建指挥者结构体,并定义其中需要使用的建造者对象和建造过程

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    // Director 构建指挥者
    type Director struct {
    builder Builder
    }

    // NewDirector 传入具体构建者,创建一个构建指挥者
    func NewDirector(builder Builder) *Director {
    return &Director{builder: builder}
    }

    func (d *Director) Construct() {
    d.builder.BuildBattery()
    d.builder.BuildElectricMotor()
    d.builder.BuildElectronicControl()
    }

单元测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
var ExpectBydCar = Car{"BYD Battery", "BYD Electric Motor", "BYD Electronic Control"}
var ExpectTeslaCar = Car{"Tesla Battery", "Tesla Electronic Motor", "Tesla Electronic Control"}

func TestBuilderBYD(t *testing.T) {
//1. BYD图纸
byd := new(BYD)
//2. BYD工厂
director := NewDirector(byd)
//3. 生产BYD汽车
director.Construct()
//4. 客户拿到BYD汽车
res := byd.GetCar()
if reflect.DeepEqual(res, ExpectBydCar) {
t.Fatalf("Builder1 fail expect 123 acture %s", res)
}
}

func TestBuilderTesla(t *testing.T) {
//1. Tesla图纸
byd := new(Tesla)
//2. Tesla工厂
director := NewDirector(byd)
//3. 生产Tesla汽车
director.Construct()
//4. 客户拿到Tesla汽车
res := byd.GetCar()
if reflect.DeepEqual(res, ExpectTeslaCar) {
t.Fatalf("Builder1 fail expect 123 acture %s", res)
}
}

可选参数

类似于可选参数的应用,可能更好理解,Option 是一种函数式编程技巧,这里也需要构建一个产品,并定义其中的属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// Product 是要创建的产品结构体
type Product struct {
part1 string
part2 string
part3 string
}

// Option 是可选参数的类型
type Option func(*Product)

// Part1Option 是 part1 字段的设置函数
func Part1Option(value string) Option {
return func(p *Product) {
p.part1 = value
}
}

// Part2Option 是 part2 字段的设置函数
func Part2Option(value string) Option {
return func(p *Product) {
p.part2 = value
}
}

// Part3Option 是 part3 字段的设置函数
func Part3Option(value string) Option {
return func(p *Product) {
p.part3 = value
}
}

// NewProduct 是创建 Product 的函数,通过传入可选参数并返回 Product 的指针
func NewProduct(opts ...Option) *Product {
product := &Product{}
// 遍历所有可选参数,并逐一将其设置给 Product 结构体的字段
for _, opt := range opts {
opt(product)
}
return product
}

在使用的时候传入需要的可选参数即可

1
2
product1 := NewProduct(Part1Option("part1"))
product2 := NewProduct(Part2Option("part2"), Part3Option("part3"))

总结

  1. 建造者模式适用于对象创建成本比较大需要经过复杂计算的情况

参考文档

  1. https://lailin.xyz/post/builder.html
  2. https://refactoringguru.cn/design-patterns/catalog
  3. https://github.com/senghoo/golang-design-pattern