设计模式-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类,使得类文件较多。
相关推荐
渊渟岳5 分钟前
掌握设计模式--装饰模式
设计模式
zh路西法2 小时前
【C++决策和状态管理】从状态模式,有限状态机,行为树到决策树(二):从FSM开始的2D游戏角色操控底层源码编写
c++·游戏·unity·设计模式·状态模式
夏旭泽3 小时前
设计模式-备忘录模式
设计模式·备忘录模式
蓝染-惣右介3 小时前
【23种设计模式·全精解析 | 行为型模式篇】11种行为型模式的结构概述、案例实现、优缺点、扩展对比、使用场景、源码解析
java·设计模式
捕鲸叉7 小时前
C++软件设计模式之类型模式和对象型模式
开发语言·c++·设计模式
诸葛悠闲7 小时前
设计模式——组合模式
设计模式·组合模式
诸葛悠闲8 小时前
设计模式——装饰模式
设计模式
西岭千秋雪_8 小时前
设计模式の中介者&发布订阅&备忘录模式
java·观察者模式·设计模式·中介者模式·备忘录模式
捕鲸叉8 小时前
C++软件设计模式之代理(Proxy)模式
c++·设计模式