创建型设计模式-生成器模式

什么是生成器模式

使你能够分步骤创建复杂对象。 该模式允许你使用相同的创建代码生成不同类型和形式的对象

  1. 生成器(Builder)接口声明在所有类型生成器中通用的产品 构造步骤

  2. 具体生成器(Concrete Builders)提供构造过程的不同实现。 具体生成器也可以构造不遵循通用接口的产品。

  3. 产品(Products)是最终生成的对象。由不同生成器构造的 产品无需属于同一类层次结构或接口。

  4. 主管(Director)类定义调用构造步骤的顺序,这样你就可以 创建和复用特定的产品配置。

  5. 客户端(Client)必须将某个生成器对象与主管类关联。一 般情况下,你只需通过主管类构造函数的参数进行一次性关 联即可。此后主管类就能使用生成器对象完成后续所有的构 造任务。但在客户端将生成器对象传递给主管类制造方法时 还有另一种方式。在这种情况下,你在使用主管类生产产品 时每次都可以使用不同的生成器

Example

生成器接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package main

type IBuilder interface {
setWindowType()
setDoorType()
setNumFloor()
getHouse() House
}

func getBuilder(builderType string) IBuilder {
if builderType == "normal" {
return newNormalBuilder()
}

if builderType == "igloo" {
return newIglooBuilder()
}
return nil
}

具体生成器A

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
package main

type NormalBuilder struct {
windowType string
doorType string
floor int
}

func newNormalBuilder() *NormalBuilder {
return &NormalBuilder{}
}

func (b *NormalBuilder) setWindowType() {
b.windowType = "Wooden Window"
}

func (b *NormalBuilder) setDoorType() {
b.doorType = "Wooden Door"
}

func (b *NormalBuilder) setNumFloor() {
b.floor = 2
}

func (b *NormalBuilder) getHouse() House {
return House{
doorType: b.doorType,
windowType: b.windowType,
floor: b.floor,
}
}

具体生成器B

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
package main

type IglooBuilder struct {
windowType string
doorType string
floor int
}

func newIglooBuilder() *IglooBuilder {
return &IglooBuilder{}
}

func (b *IglooBuilder) setWindowType() {
b.windowType = "Snow Window"
}

func (b *IglooBuilder) setDoorType() {
b.doorType = "Snow Door"
}

func (b *IglooBuilder) setNumFloor() {
b.floor = 1
}

func (b *IglooBuilder) getHouse() House {
return House{
doorType: b.doorType,
windowType: b.windowType,
floor: b.floor,
}
}

产品

1
2
3
4
5
6
7
package main

type House struct {
windowType string
doorType string
floor int
}

主管

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package main

type Director struct {
builder IBuilder
}

func newDirector(b IBuilder) *Director {
return &Director{
builder: b,
}
}

func (d *Director) setBuilder(b IBuilder) {
d.builder = b
}

func (d *Director) buildHouse() House {
d.builder.setDoorType()
d.builder.setWindowType()
d.builder.setNumFloor()
return d.builder.getHouse()
}

客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package main

import "fmt"

func main() {
normalBuilder := getBuilder("normal")
iglooBuilder := getBuilder("igloo")

director := newDirector(normalBuilder)
normalHouse := director.buildHouse()

fmt.Printf("Normal House Door Type: %s\n", normalHouse.doorType)
fmt.Printf("Normal House Window Type: %s\n", normalHouse.windowType)
fmt.Printf("Normal House Num Floor: %d\n", normalHouse.floor)

director.setBuilder(iglooBuilder)
iglooHouse := director.buildHouse()

fmt.Printf("\nIgloo House Door Type: %s\n", iglooHouse.doorType)
fmt.Printf("Igloo House Window Type: %s\n", iglooHouse.windowType)
fmt.Printf("Igloo House Num Floor: %d\n", iglooHouse.floor)

}

适用场景

  1. 使 用 生 成 器 模 式 可 避 免 “重 叠 构 造 函 数 (telescopic constructor)”的出现
  • 使 用 生 成 器 模 式 可 避 免 “重 叠 构 造 函 数 (telescopic constructor)”的出现
  1. 当你希望使用代码创建不同形式的产品(例如石头或木头房 屋)时,可使用生成器模式。
  • 如果你需要创建的各种形式的产品,它们的制造过程相似且 仅有细节上的差异,此时可使用生成器模式。
  1. 使用生成器构造组合树或其他复杂对象
  • 生成器模式让你能分步骤构造产品。你可以延迟执行某些步 骤而不会影响最终产品。你甚至可以递归调用这些步骤,这 在创建对象树时非常方便。

优缺点

优点

  • 你可以分步创建对象,暂缓创建步骤或递归运行创建步骤。

  • 生成不同形式的产品时,你可以复用相同的制造代码

  • 单一职责原则。你可以将复杂构造代码从产品的业务逻辑中 分离出来。

缺点

  • 由于该模式需要新增多个类,因此代码整体复杂程度会有所 增加

与其他模式的关系

  • 在许多设计工作的初期都会使用工厂方法(较为简单,而且 可以更方便地通过子类进行定制),随后演化为使用抽象工 厂、原型或生成器(更灵活但更加复杂)。
  • 生成器重点关注如何分步生成复杂对象。抽象工厂专门用于 生产一系列相关对象。抽象工厂会马上返回产品,生成器则 允许你在获取产品前执行一些额外构造步骤
  • 你可以在创建复杂组合树时使用生成器,因为这可使其构造 步骤以递归的方式运行
  • 你可以结合使用生成器和桥接模式: 主管类负责抽象工作, 各种不同的生成器负责实现工作
  • 抽象工厂、生成器和原型都可以用单例来实现