设计模式-04.建造者模式

背景

假设有一个需求:需要生产一辆汽车,汽车需要引擎、车轮、方向盘、车身等,不同品牌的汽车引擎、车轮等各不相同。现在,用户需要一辆宝马汽车,可能代码会这样写:

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

举例说明

还是以制造汽车为例,下面以建造者模式来改造代码。

  1. 产品(这里就是汽车)
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
    }
}
  1. 抽象建造者(构造一个汽车必须要有引擎、车轮、方向盘、车身)
swift 复制代码
protocol Builder {
    var car: Car { set get }
    func buildEngine()
    func buildWheel()
    func buildSteeringWheel()
    func buildBody()
}
  1. 具体建造者(宝马建造者和奔驰建造者,都实现了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: "奔驰车身")
    }

}
  1. 指挥者(Director)
swift 复制代码
class Director {
    func construct(builder: Builder) {
        builder.buildEngine()
        builder.buildWheel()
        builder.buildSteeringWheel()
        builder.buildBody()
    }
}

假如没有Director这一层,那么外部调用就必须要知道Builder有哪些方法。Director作用:控制建造过程,用来隔离用户与建造过程的关联

  1. 客户端调用
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) // 汽车的组成部件:奔驰引擎、奔驰车轮、奔驰方向盘、奔驰车身、
}
  1. 客户端调用(不使用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)
}

总结

优点:

  1. 建造者模式,使得建造代码与表示代码分离,由于隐藏了该产品是如何组装的,所以如果需要改变一个产品的内部表示,只需要再定义一个具体的建造者就可以了。
  2. 将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰,也更方便使用程序来控制创建过程。
  3. 增加新的建造者无需修改原有类的代码,指挥者针对抽象建造者编程,扩展方便,复合'开闭原则'。

缺点

  1. 建造者模式创建的产品一般都是具有较多的共同点,组成部分都很相似,如果产品的组成部分差距过多,则不适合使用建造者。
  2. 如果产品相当复杂,比如组成部分有很多,则Builder类中就需要抽取更多的代码,以及可能会产生很多具体的Buidler类,使得类文件较多。
相关推荐
cijiancao8 小时前
23 种设计模式中的解释器模式
java·设计模式·解释器模式
南七行者8 小时前
对模板方法模式的理解
java·设计模式·模板方法
01空间16 小时前
设计模式简述(四)模板方法模式
设计模式
都叫我大帅哥20 小时前
代码世界的「万能转接头」:适配器模式的跨界艺术
java·后端·设计模式
Niuguangshuo21 小时前
Python 设计模式:迭代模式
java·python·设计模式
十五年专注C++开发1 天前
QT 中的元对象系统(五):QMetaObject::invokeMethod的使用和实现原理
开发语言·数据结构·c++·qt·设计模式
shuaixio1 天前
【C++代码整洁之道】第九章 设计模式和习惯用法
c++·设计模式·设计原则·常见设计模式·习惯用法
南宫生1 天前
Java迭代器【设计模式之迭代器模式】
java·学习·设计模式·kotlin·迭代器模式
程序员小赵同学1 天前
AI Agent设计模式二:Parallelization
开发语言·python·设计模式
千千寰宇1 天前
[设计模式/Java] 设计模式之工厂方法模式【11】
设计模式