设计模式-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类,使得类文件较多。
相关推荐
nakyoooooo43 分钟前
【设计模式】工厂模式、单例模式、观察者模式、发布订阅模式
观察者模式·单例模式·设计模式
严文文-Chris2 小时前
【设计模式-享元】
android·java·设计模式
丶白泽3 小时前
重修设计模式-设计原则
设计模式·接口隔离原则·依赖倒置原则·开闭原则
【D'accumulation】3 小时前
典型的MVC设计模式:使用JSP和JavaBean相结合的方式来动态生成网页内容典型的MVC设计模式
java·设计模式·mvc
仙魁XAN4 小时前
Unity 设计模式 之 创造型模式-【工厂方法模式】【抽象工厂模式】
unity·设计模式·工厂方法模式·抽象工厂模式
龙哥·三年风水14 小时前
活动系统开发之采用设计模式与非设计模式的区别-后台功能总结
设计模式·php·tinkphp6
一头老羊15 小时前
前端常用的设计模式
设计模式
严文文-Chris16 小时前
【设计模式-组合】
设计模式
kimloner17 小时前
工厂模式(二):工厂方法模式
java·设计模式·工厂方法模式
丶白泽19 小时前
重修设计模式-结构型-桥接模式
java·设计模式·桥接模式