设计模式-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类,使得类文件较多。
相关推荐
Doris_20232 小时前
代码格式化 使用oxfmt
设计模式·架构·前端框架
Doris_20232 小时前
说一说ESLint+Prettier生效的原理
前端·设计模式·架构
Pomelooooo3 小时前
把 git commit 这件事,彻底交给 AI ——一个工程化 /git-commit 命令的设计与落地
设计模式
invicinble4 小时前
设计模式(类的拓扑结构)(描述总纲)
设计模式·原型模式
invicinble7 小时前
设计模式(类的拓扑结构)(为什么会产生设计模式,以及什么是设计模式)
linux·服务器·设计模式
PersonalViolet9 小时前
模板方法模式实战:重构Agent工具审批,告别重复代码
设计模式·agent
老码观察9 小时前
设计模式实战解读(五):策略模式——干掉 if-else 的优雅方案
java·设计模式·策略模式
解决问题no解决代码问题10 小时前
设计模式分类介绍
java·开发语言·设计模式
烬羽10 小时前
从 Python List 到 LLM 接口:一条被忽视的 AI 入门捷径
设计模式
我爱cope11 小时前
【Agent智能体8 | 反思设计模式-大语言模型反思机制的四个演进阶段】
人工智能·设计模式·语言模型