闲话设计模式之建造者模式

165 阅读4分钟

风海: 铜锣老兄,我最近买了台不错的电脑诶,你要不要看看配置?

铜锣: 来来,我过目过目。

风海: 你看看啊。

类型配置
主体Intel 天逸510S
内存DDR4
视频接口VGA/HDMI
CPU4核i3
显卡集成
硬盘西部数据7200rpm
输入设备罗技鼠标+硬盘
显示器三星xx型号

铜锣: 感觉不错嘛,回头拿过来跑跑分。

风海: 其实有一些参数我还没放上来呢,我在想,如果有一天我是负责写代码处理这一块的录入,那我建一个Computer类岂不是构造函数要包含这么一大坨数据。

铜锣: 啊哈,你这个类真包含这么大坨数据的话,它就是典型的复杂构建问题了。

风海: 哦?那么对于复杂对象的构建,有什么好的解决方案吗?

铜锣: 设计模式倒是有这么一个模式是专门解决这个问题的,叫建造者模式,或者叫生成器模式,还有一些别的译名,不过英文名倒是统一的,叫Builder Pattern

风海: 说到创建对象,我们之前也聊过工厂模式,这个建造者模式也负责创建对象,两者有什么区别吗?

铜锣: 当然有区别,工厂模式解决的是类型多样性的问题,建造者模式解决的是类型内部结构复杂性的问题。

风海: 这样,那你以代码为例,如果把我这台电脑写作一个类Computer,根据客户提供的信息进行个性化配置,那么这个类应该怎么写。

铜锣: 我先从基础写起,比如这样:

class Computer {
    var body: String = ""
    var memory: String = ""

    var description: String {
        "body = \(body) memory = \(memory)"
    }
}

风海: 看到了,你写了一个Computer类,提供了两个属性,然后呢,没看到有Builder啊。

风海: 别急,这只是起步,我往下写。

class Computer {
    var body: String = ""
    var memory: String = ""
    var description: String {
        "body = \(body) memory = \(memory)"
    }
    static func builder() -> Builder {
        return Builder()
    }
    class Builder {
        let computer = Computer()
        @discardableResult
        func set(body: String) -> Builder {
            computer.body = body
            return self
        }
        @discardableResult
        func set(memory: String) -> Builder {
            computer.memory = memory
            return self
        }
        func build() -> Computer {
            return computer
        }
    }
}

现在,我们定义了一个Computer类,这个类的创建并不由自己完成,而是由Computer内部的一个Builder类完成,有意思吧?

风海: 理解了,所以说建造者模式要求复杂的类内部要有个Builder吗?

铜锣: 哦不不,Builder具体放在那里不是设计模式规定的,设计模式只是规划了个总体思想,具体实现是由开发者根据实际情况定夺的。

在这个例子里,在我们在创建Computer对象的时候,可以这么写

let builder = Computer.builder()
builder.set(body: "Intel 天逸510S")
builder.set(memory: "DDR4")
let computer = builder.build()
print("\(computer.description)")

风海: 原来如此,这时候Computer的创建被延后了,交给Builder去逐步构建属性,等构建成型后再直接创建Computer,这么写倒不错,但是代码行数还挺多的。

铜锣: 是的,所以你有没有看到,Builderset代码都会返回Builder呢,就是为了便捷性,事实上Computer的创建还可以这么写。

let computer = Computer.builder()
    .set(body: "Intel 天逸510S")
    .set(memory: "DDR4")
    .build()
print("\(computer.description)")

风海: 原来如此,这样确实便捷了很多。所以那个@discardableResult也是为了单纯属性设置而不打算接收返回值时不会收到编译器警告而设定的。

铜锣: 对,没错。我们还可以进一步扩展,比方说我们通过Builder来构建Computer,那么当Builder不断增加时,我们也可以做一些Builder模板,比如如下代码:

class HPComputerBuilder: Computer.Builder {
    override init() {
        super.init()
        set(body: "HP 超级芯片").set(memory: "DDR4")
    }
}

风海: 明白了,这样一来通过单独定义的Builder类,我们可以非常便捷的直接构建定制的Computer

铜锣: 是的。当需要使用时我们这么写:

let hpComputer = HPComputerBuilder().build()
print("\(hpComputer.description)")

风海: 嗯,我们聊了这么多,好像连建造者模式的定义都没给呢。

铜锣: 是啊,定义留给最后嘛。

生成器模式(英:Builder Pattern)是一种设计模式,又名:建造模式,是一种对象构建模式。它可以将复杂对象的建造过程抽象出来(抽象类别),使这个抽象过程的不同实现方法可以构造出不同表现(属性)的对象。

风海: 好了,三点几了,饮茶去了。

铜锣: 欧耶。