建造者模式¶
分类:创建型模式。
类结构¶
classDiagram
direction TB
class Director {
+construct()
}
class Builder {
<<interface>>
+buildPartA()
+buildPartB()
}
class ConcreteBuilder {
-Product product
+buildPartA()
+buildPartB()
+getResult() Product
}
class Product
Director --> Builder : 编排构建步骤
Builder <|.. ConcreteBuilder
ConcreteBuilder --> Product : 构建并持有
角色定义¶
| 角色 | 定义 |
|---|---|
| Director(指挥者) | 通过建造者接口安排构建步骤的顺序;需要复用固定构建流程时可使用这个角色。 |
| Builder(建造者接口) | 声明构建产品各部分的操作,使构建过程可以调用统一接口。 |
| ConcreteBuilder(具体建造者) | 实现各步骤,保存构建中的产品,并通过 getResult() 提供结果。不同实现可以构建不同表示。 |
| Product(产品) | 由多个构建步骤逐步组装出来的对象。 |
Head First 示例:逐步组装度假计划¶
本节对应《Head First 设计模式》附录中的建造者主题,具体类名与调用方式依据作者 2020 年第二版配套代码 builder/vacation 整理。这个实现用同一组建造操作安排两类度假计划:户外方案添加帐篷和徒步活动,城市方案添加酒店和演出活动,最后都得到 Vacation 对象。这里的类图是根据配套实现绘制的示意图。
VacationDirector 的实际职责是示例装配与调用:它只有 main(),在其中选择两种具体建造者,安排链式调用并打印结果。VacationBuilder 是抽象类,声明三个 addAccommodation() 重载和 addEvent(),由 getVacation() 创建结果;源码没有通用图中的 construct()、buildPartA() 或 getResult() 方法。建造者先累计住宿和活动数据,再把它们交给结果对象。
| 具体类名 | 对应角色 | 职责 |
|---|---|---|
VacationDirector |
示例装配入口与指挥者 | 在 main() 中创建建造者、安排构建步骤、取得并打印度假计划。 |
VacationBuilder |
抽象建造者 | 保存名称、住宿列表和活动列表,声明添加步骤并实现 getVacation()。 |
OutdoorsVacationBuilder |
户外具体建造者 | 创建 Tent,将活动描述加工为徒步活动。 |
CityVacationBuilder |
城市具体建造者 | 创建 Hotel,将活动描述加工为观看演出。 |
Vacation |
构建结果 | 接收名称与两组列表,通过 toString() 展示度假计划。 |
Accommodation |
住宿抽象基类 | 保存住宿名称与可选预订,声明 getLocation()。 |
Tent |
户外住宿组成部分 | 保存营位号,通过 getLocation() 描述位置。 |
Hotel |
城市住宿组成部分 | 保存房间号,通过 getLocation() 描述位置。 |
Reservation |
预订数据组成部分 | 保存入住日期和住宿晚数。 |
示例 UML¶
图中保留 9 个类型,省略集合与日期库类型、部分访问器,以及无参和带日期等参数的 addAccommodation() 重载;具体子类重复实现的方法签名也作了省略。* 表示抽象方法,$ 表示静态方法,~ 表示包级可见。住宿列表会同时被建造者和结果引用,因此用普通关联表达持有关系。
classDiagram
direction TB
class VacationDirector {
+main(String[] args) void$
}
class VacationBuilder {
<<abstract>>
~String name
+addAccommodation(String name) VacationBuilder*
+addEvent(String event) VacationBuilder*
+getVacation() Vacation
}
class OutdoorsVacationBuilder {
+addAccommodation(String name) VacationBuilder
+addEvent(String event) VacationBuilder
}
class CityVacationBuilder {
+addAccommodation(String name) VacationBuilder
+addEvent(String event) VacationBuilder
}
class Vacation {
+setName(String name) void
+setAccommodations(List~Accommodation~ accommodations) void
+setEvents(List~String~ events) void
+toString() String
}
class Accommodation {
<<abstract>>
~String name
+setReservation(Reservation r) void
+getReservation() Reservation
+getLocation() String*
}
class Tent {
~int siteNumber
+setSiteNumber(int n) void
+getLocation() String
}
class Hotel {
~int roomNumber
+setRoomNumber(int n) void
+getLocation() String
}
class Reservation {
+setArrivalDate(int year, int month, int day) void
+setNights(int nights) void
}
VacationDirector ..> VacationBuilder : 编排步骤并取得结果
VacationDirector ..> OutdoorsVacationBuilder : 创建
VacationDirector ..> CityVacationBuilder : 创建
VacationBuilder <|-- OutdoorsVacationBuilder
VacationBuilder <|-- CityVacationBuilder
VacationBuilder ..> Vacation : 创建并设置数据
VacationBuilder --> "0..*" Accommodation : accommodations
Vacation --> "0..*" Accommodation : accommodations
Accommodation <|-- Tent
Accommodation <|-- Hotel
Accommodation --> "0..1" Reservation : reservation
OutdoorsVacationBuilder ..> Tent : 创建
CityVacationBuilder ..> Hotel : 创建
协作过程¶
VacationDirector.main()先创建OutdoorsVacationBuilder并通过VacationBuilder引用调用它,随后另建CityVacationBuilder装配城市方案;两条调用链分别定义各自的住宿和活动安排。- 带
name、year、month、day、nights、location参数的addAccommodation()会创建Reservation并设置日期和晚数。户外实现将它交给Tent,用setSiteNumber()设置营位;城市实现交给Hotel,用setRoomNumber()设置房间。只传名称的重载则不创建预订。 addEvent()把活动转换成对应方案的文字并加入events。添加住宿和活动的方法都返回当前建造者,所以示例能连续链式调用。getVacation()每次新建Vacation,调用setName()、setAccommodations()和setEvents(),再把结果交回入口打印。此实现直接传递已有列表,既没有复制列表,也没有重置建造者;继续添加内容会影响此前结果中的共享列表。
固定版本源码:
- 示例装配与调用:VacationDirector
- 抽象步骤与结果生成:VacationBuilder
- 户外方案:OutdoorsVacationBuilder
- 城市方案:CityVacationBuilder