背景
假设有一个需求:需要生产一辆汽车,汽车需要引擎、车轮、方向盘、车身等,不同品牌的汽车引擎、车轮等各不相同。现在,用户需要一辆宝马汽车,可能代码会这样写:
swift
struct Car {
var parts = [String]()
mutating func add(part: String) {
parts.append(part)
}
}
class BWMCarBuilder {
func build() -> Car {
var car = Car()
car.add(part: "宝马引擎")
car.add(part: "宝马车轮")
car.add(part: "宝马方向盘")
car.add(part: "宝马车身")
return car
}
}
那如果用户需要购买一台奔驰汽车,那我们可以再新建一个BenchiBuilder
,来创建汽车:
swift
class BenchiBuilder {
func build() -> Car {
var car = Car()
car.add(part: "奔驰引擎")
car.add(part: "奔驰车轮")
car.add(part: "奔驰方向盘")
car.add(part: "奔驰车身")
return car
}
}
目前,已经可以得到宝马
和奔驰
汽车,但是这里面有个问题,可能会存在编程不注意,汽车会少添加一个部分,比如忘记添加引擎或车身等。最好的办法就是,凡是制造汽车,都必须要有引擎、车轮、方向盘及车身。
建造者模式
上述的问题我们可以通过建造者模式来优化。制造汽车的过程 是稳定的,都需要引擎、车轮、方向盘及车身,只是具体的制造细节 不同。但对于用户来说,只需要告诉制造商,我需要一辆宝马,制造商只需要给一辆宝马就行了,用户不关心制造车的具体过程以及细节。如果需要将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示的意图时 ,就可以使用'建造者模式 ',又可以叫'生成器模式'。使用了建造者模式,用户只需要指定建造的类型就可以得到想要的产品,而具体的建造过程和细节不需要知道。
建造者模式:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
主要角色与关系
- Builder:创建一个Product对象的各个部件指定的抽象接口
- ConcreteBuilder:具体建造者,实现了Builder接口,构造和装配各个部件
- Director:指挥建造过程
- Product:产品角色
UML
classDiagram
direction BT
class Director {
-construct()
}
class Builder {
<>
-buildEngine()
-buildWheel()
-buildSteeringWheel()
-buildBody()
}
class BWMBuilder {
-buildEngine()
-buildWheel()
-buildSteeringWheel()
-buildBody()
}
class BenchiBuilder {
-buildEngine()
-buildWheel()
-buildSteeringWheel()
-buildBody()
}
class Car
Director o--> Builder : builder
BenchiBuilder ..|> Builder
BWMBuilder ..|> Builder
BWMBuilder ..> Car
BenchiBuilder ..> Car
举例说明
还是以制造汽车为例,下面以建造者模式来改造代码。
- 产品(这里就是汽车)
swift
struct Car: CustomStringConvertible {
var parts = [String]()
mutating func add(part: String) {
parts.append(part)
}
var description: String {
var res = "汽车的组成部件:"
parts.forEach { part in
res += part + "、"
}
return res
}
}
- 抽象建造者(构造一个汽车必须要有引擎、车轮、方向盘、车身)
swift
protocol Builder {
var car: Car { set get }
func buildEngine()
func buildWheel()
func buildSteeringWheel()
func buildBody()
}
- 具体建造者(宝马建造者和奔驰建造者,都实现了
Builder
的接口)
swift
class BWMBuilder: Builder {
var car: Car = Car()
func buildEngine() {
car.add(part: "宝马引擎")
}
func buildWheel() {
car.add(part: "宝马车轮")
}
func buildSteeringWheel() {
car.add(part: "宝马方向盘")
}
func buildBody() {
car.add(part: "宝马车身")
}
}
class BenchiBuilder: Builder {
var car: Car = Car()
func buildEngine() {
car.add(part: "奔驰引擎")
}
func buildWheel() {
car.add(part: "奔驰车轮")
}
func buildSteeringWheel() {
car.add(part: "奔驰方向盘")
}
func buildBody() {
car.add(part: "奔驰车身")
}
}
- 指挥者(Director)
swift
class Director {
func construct(builder: Builder) {
builder.buildEngine()
builder.buildWheel()
builder.buildSteeringWheel()
builder.buildBody()
}
}
假如没有Director
这一层,那么外部调用就必须要知道Builder
有哪些方法。Director
作用:控制建造过程,用来隔离用户与建造过程的关联
- 客户端调用
swift
func testBuilderClient() {
let director = Director()
let bwmBuilder = BWMBuilder()
director.construct(builder: bwmBuilder)
let bwmCar = bwmBuilder.car
print(bwmCar) // 汽车的组成部件:宝马引擎、宝马车轮、宝马方向盘、宝马车身、
let benchiBuilder = BenchiBuilder()
director.construct(builder: benchiBuilder)
let benchiCar = benchiBuilder.car
print(benchiCar) // 汽车的组成部件:奔驰引擎、奔驰车轮、奔驰方向盘、奔驰车身、
}
- 客户端调用(不使用
Director
)
swift
func testBuilderClient() {
let bmwBuilder = BWMBuilder()
bmwBuilder.buildEngine()
bmwBuilder.buildWheel()
bmwBuilder.buildSteeringWheel()
bmwBuilder.buildBody()
let bwmCar = bmwBuilder.car
print(bwmCar)
let benchiBuilder = BenchiBuilder()
benchiBuilder.buildEngine()
benchiBuilder.buildWheel()
benchiBuilder.buildSteeringWheel()
benchiBuilder.buildBody()
let benchiCar = benchiBuilder.car
print(benchiCar)
}
总结
优点:
- 建造者模式,使得建造代码与表示代码分离,由于隐藏了该产品是如何组装的,所以如果需要改变一个产品的内部表示,只需要再定义一个具体的建造者就可以了。
- 将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰,也更方便使用程序来控制创建过程。
- 增加新的建造者无需修改原有类的代码,指挥者针对抽象建造者编程,扩展方便,复合'开闭原则'。
缺点
- 建造者模式创建的产品一般都是具有较多的共同点,组成部分都很相似,如果产品的组成部分差距过多,则不适合使用建造者。
- 如果产品相当复杂,比如组成部分有很多,则
Builder
类中就需要抽取更多的代码,以及可能会产生很多具体的Buidler类,使得类文件较多。