设计模式-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 { <<interface>> -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类,使得类文件较多。
相关推荐
workflower2 小时前
情境感知系统
人工智能·机器学习·设计模式·自然语言处理·机器人
咖啡八杯4 小时前
设计模式中的原则
设计模式·接口隔离原则·里氏替换原则·开闭原则·合成复用原则·单一职责原则
安冬的码畜日常6 小时前
【工欲善其事】深入理解 Node.js 带并发上限的异步任务批量执行逻辑
javascript·设计模式·node.js·ai编程·异步编程·并发执行
AI大法师17 小时前
一个机场如何被做成 IP 场景:宝可梦机场的设计方法总结
大数据·人工智能·设计模式·新媒体运营
晚安code21 小时前
干掉成山的 if-else:工厂造、策略选,一文讲透两个模式的配合
后端·设计模式
workflower1 天前
从幻觉,到现实
人工智能·深度学习·机器学习·设计模式·机器人
电子科技圈1 天前
第二代无线平台历久弥新,赋能物联网创新迭代
人工智能·嵌入式硬件·mcu·物联网·设计模式·硬件架构·iot
choumin2 天前
创建型模式——工厂方法模式
c++·设计模式·工厂方法模式·创建型模式
choumin2 天前
创建型模式——原型模式
c++·设计模式·原型模式·创建型模式
37.2℃9952 天前
Claude Design哪个公司技术好
python·设计模式