设计模式-建造者模式

Builder

建造者模式 (生成器模式) 使用相同的创建代码生成不同类型和形式的对象、分步骤创建复杂对象

  • 构造具有复杂内部属性的对象
  • 构造不同类型或形式的对象
  • 若内部属性简单会造成代码冗余

以构造正方体为例,不使用建造者模式时

1
2
3
4
5
6
7
// create three cube
// cube 1: vertices1, color1, material1
// cube 2: vertices1, color2
// cube 3: vertices2, material2
Cube cube1(vertices1, color1, material1);
Cube cube2(vertices1, color2, material1);
Cube cube3(vertices2, color1, material2);

使用建造者模式时

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Cube cube1 = CubeBuilder()
.withVertices(vertices1)
.withColor(color1)
.withMaterial(material1)
.build();

Cube cube2 = CubeBuilder()
.withVertices(vertices1)
.withColor(color2)
.build();

Cube cube3 = CubeBuilder()
.withVertices(vertices2)
.withMaterial(material2)
.build();

对比使用建造者模式前后差别

性质 直接模式 建造者模式
对象构造 构造所需属性参数多 逐步构造对象
可读性 难以追踪属性参数用途 属性参数用途清晰
灵活性 构造多个对象需要重复代码 易创建不同属性组合的对象
维护性 添加属性需要修改构造代码 添加属性无需修改构造代码

CPP

基本结构

Member Full Name Description
产品类 Product 包含多个部分或属性的复杂对象
抽象建造者 Abstract Builder 产品构建方法的抽象接口
具体建造者 Concrete Builder 具体实现产品各部分或属性的构建方法
指导者 Director 定义构建顺序,调用建造方法构建产品

UML结构图

实现步骤

  1. 定义产品类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Product {
private:
std::vector<std::string> parts;
int id;
std::string name;

public:
Product(int id, const std::string& name) : id(id), name(name) {}

void AddPart(const std::string& part) {
parts.push_back(part);
}

void ShowParts() const {
std::cout << "Product parts:" << std::endl;
for (const auto& part : parts) {
std::cout << part << std::endl;
}
std::cout << "ID: " << id << ", Name: " << name << std::endl;
}
};
  1. 定义抽象建造者
1
2
3
4
5
6
7
class Builder {
public:
virtual ~Builder() = default;
virtual void BuildPartA() = 0;
virtual void BuildPartB() = 0;
virtual Product GetProduct() const = 0;
};
  1. 定义具体建造者
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
41
42
43
44
class ConcreteBuilder1 : public Builder {
private:
Product* product;

public:
ConcreteBuilder1() : product(nullptr) {}
void ResetProduct() {
product = new Product(101, "Product Type 1");
}
void BuildPartA() override {
product->AddPart("PartA (Type 1)");
}
void BuildPartB() override {
product->AddPart("PartB (Type 1)");
}
Product GetProduct() const override {
return *product;
}
};

class ConcreteBuilder2 : public Builder {
private:
Product* product;

public:
ConcreteBuilder2() : product(nullptr) {}

void ResetProduct() {
product = new Product(202, "Product Type 2");
}

void BuildPartA() override {
product->AddPart("PartA (Type 2)");
}

void BuildPartB() override {
product->AddPart("PartB (Type 2)");
product->AddPart("ExtraPartB (Type 2)");
}

Product GetProduct() const override {
return *product;
}
};
  1. 定义指导者
1
2
3
4
5
6
7
8
9
10
11
12
13
class Director {
private:
Builder* builder;

public:
Director(Builder* builder) : builder(builder) {}

Product Construct() {
builder->BuildPartA();
builder->BuildPartB();
return builder->GetProduct();
}
};
  1. 客户代码
1
2
3
4
5
6
7
8
9
10
11
ConcreteBuilder1 builder1;
builder1.ResetProduct();
Director director1(&builder1);
Product product1 = director1.Construct();
product1.ShowParts();

ConcreteBuilder2 builder2;
builder2.ResetProduct();
Director director2(&builder2);
Product product2 = director2.Construct();
product2.ShowParts();

基于 Fluent Interface 实现链式调用(Method Chaining)来提高代码的可读性和可维护性

  1. 定义产品类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Product {
private:
std::vector<std::string> parts;
int id;
std::string name;

public:
Product(int id, const std::string& name) : id(id), name(name) {}

void AddPart(const std::string& part) {
parts.push_back(part);
}

void ShowParts() const {
std::cout << "Product parts:" << std::endl;
for (const auto& part : parts) {
std::cout << part << std::endl;
}
std::cout << "ID: " << id << ", Name: " << name << std::endl;
}
};
  1. 定义建造者
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class ChainBuilder {
private:
Product product;

public:
ChainBuilder(int id, const std::string& name)
: product(id, name) {}

ChainBuilder* AddPart(const std::string& part) {
product.AddPart(part);
return this;
}

Product Build() {
return product;
}
};
  1. 客户代码
1
2
3
4
5
6
Product product1 = ChainBuilder(101, "Product 1")
.AddPart("PartA")
.AddPart("PartB")
.AddPart("PartC")
.Build();
product1.ShowParts();

Fortran

以下代码只尽量确保与 CPP 形式一致,不一定是最佳实践

基于 CPP 链式模式修改,相较于经典模式更灵活,但 Fortran 不支持 Fluent Interface 因此只能逐个定义

product part 基于链表实现,需注意是否存在内存泄露,此处未做检查

  1. 定义产品类
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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
module product_class
implicit none
private

public :: ProductClass

type :: PartList
character(len=256) :: data
type(PartList), pointer :: next => null()
end type PartList

type :: ProductClass
integer :: id
character(len=256) :: name
type(PartList), pointer :: parts => null()
contains
procedure, pass, public :: add_part => add_part
procedure, pass, public :: show_parts => show_parts
final :: destructor
end type ProductClass

contains

subroutine add_part(this, part)
class(ProductClass), intent(inout) :: this
character(len=*), intent(in) :: part
type(PartList), pointer :: new_node
type(PartList), pointer :: current

allocate(new_node)
new_node%data = part
new_node%next => null()

if (.not. associated(this%parts)) then
this%parts => new_node
else
current => this%parts
do while (associated(current%next))
current => current%next
end do
current%next => new_node
end if
end subroutine add_part

subroutine show_parts(this)
class(ProductClass), intent(in) :: this
type(PartList), pointer :: current

print *, "Product parts:"
current => this%parts
do while (associated(current))
print *, current%data
current => current%next
end do
print *, "ID:", this%id, ", Name:", this%name
end subroutine show_parts

subroutine destructor(this)
type(ProductClass), intent(inout) :: this
type(PartList), pointer :: current, next

current => this%parts
do while (associated(current))
next => current%next
deallocate(current)
current => next
end do
this%parts => null()
end subroutine destructor

end module product_class
  1. 定义建造者
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
41
module chain_builder
use product_class, only: ProductClass
implicit none

public :: ChainBuilder

type :: ChainBuilder
private
type(ProductClass) :: product
contains
procedure, public, pass :: reset_product
procedure, public, pass :: add_part
procedure, public, pass :: build
end type ChainBuilder

contains

subroutine reset_product(this, id, name)
class(ChainBuilder), intent(inout) :: this
integer, intent(in) :: id
character(len=*), intent(in) :: name
continue
this%product%id = id
this%product%name = name
this%product%parts => null()
end subroutine reset_product

subroutine add_part(this, part)
class(ChainBuilder), intent(inout) :: this
character(len=*), intent(in) :: part
continue
call this%product%add_part(part)
end subroutine add_part

function build(this) result(product)
class(ChainBuilder), intent(inout) :: this
type(ProductClass) :: product
continue
product = this%product
end function build
end module chain_builder
  1. 创建产品
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
type(ChainBuilder) :: builder
type(ProductClass) :: product1, product2

call builder%reset_product(101, "Product 1")
call builder%add_part("PartA")
call builder%add_part("PartB")
call builder%add_part("PartC")
product1 = builder%build()
call product1%show_parts()

call builder%reset_product(202, "Product 2")
call builder%add_part("PartX")
call builder%add_part("PartY")
product2 = builder%build()
call product2%show_parts()

Reference

菜鸟教程 | 建造者模式
FLZJ_KL | 建造者模式
癫狂编程 | C++实现设计模式——Builder模式

0%