Swift - 生成器模式(Builder Pattern)

本文通过实例讲解了Swift中生成器模式的运用,通过KFC汉堡包的例子,展示了如何利用Builder创建对象,以及相同的建造方法但执行顺序不同带来的不同结果。
没有人买车会只买一个轮胎或者方向盘,大家买的都是一辆包含轮胎、方向盘和发动机等多个部件的完整汽车。如何将这些部件组装成一辆完整的汽车并返回给用户,这是生成器模式需要解决的问题。下面就一起来看看生成器模式

生成器模式

生成器模式(Builder Pattern)也叫:建造模式,是一种对象构建模式。
1、它可以将复杂对象的建造过与它的表现分离,使得同样的构建过程可以创建不同的表示。 
2、Builder模式是一步一步创建一个复杂的对象,它允许用户可以只通过指定复杂对象的类型和内容就可以构建它们.用户不知道内部的具体构建细节.

参与者



Builder:抽象建造者,为创建一个Product对象的各个部件指定抽象接口。在该接口中一般声明两类方法,一类方法是buildPartX(),它们用于创建复杂对象的各个部件;另一类方法是getResult(),它们用于返回复杂对象。Builder既可以是抽象类,也可以是接口,iOS中使用协议。

ConcreteBuilder:具体建造者,实现Builder的接口以构造和装配该产品的各个部件;即实现抽象builder类的所有方法,并返回一个创建好的对象。

Director:指导者,也被称导向者,构造一个使用Builder接口的对象它负责安排复杂对象的建造次序,指挥者与抽象建造者之间存在关联关系,可以在其construct()建造方法中调用建造者对象的部件构造与装配方法,完成复杂对象的建造。客户端一般只需要与指挥者进行交互,在客户端确定具体建造者的类型,并实例化具体建造者对象(也可以通过配置文件和反射机制),然后通过指挥者类的构造函数或者Setter方法将该对象传入指挥者类中。

Product:产品,表示被构造的复杂对象,包含多个部件ConcreateBuilder创建该产品的内部表示并定义它的装配过程;包含定义组成部件的类,包括将这些部件装配成最终产品的接口。

Swift-Demo实例

简单实现生成器模式

//被构建的产品对象的接口
class Product {
    var parts: String = ""
}
//生成器接口,定义创建一个产品对象所需的各个部件的操作
//Builder模式是把复杂对象的创建和部件的创建分别开来
protocol Builder {
    //创建部件
    func buildPart()
    //返回最后组装成品结果 (返回最后装配好的汽车)
    func getProduct() -> Product
}
//指导者,指导使用生成器的接口来构建产品对象
class Director {
    let concreteBuilder: Builder
    init(concreteBuilder: Builder) {
        self.concreteBuilder = concreteBuilder
    }
    //将部件part最后组成复杂对象
    func construct() ->Product{
        concreteBuilder.buildPart()
        return concreteBuilder.getProduct()
    }
}

//Builder的具体实现ConcreteBuilder,通过具体完成接口Builder来构建或装配产品的部件;
//定义并明确它所要创建的是什么具体东西;提供一个可以重新获取产品的接口:
class ConcreteBuilder: Builder{
    
    var product = Product()
    //这里是具体如何构建partA的代码
    func buildPart() {
        product.parts = "adding part succss!"
    }
    //返回最后组装成品结果
    func getProduct() -> Product{
        return product
    }
}
使用如下:

let director = Director(concreteBuilder: ConcreteBuilder())
let product = director.construct()
print(product.parts)//adding part succss!

盖房子,从该博客翻译为Swift语言实现. 

通俗讲解:Builder模式的理解,简单地说,就好象我要一盖房子住,可是我不知道怎么盖(简单的砌墙,层次较低),也不知道怎么样设计(建几个房间,几个门好看,层次较高),于是我需要找一帮民工,他们会砌墙,还得找个设计师,他知道怎么设计,我还要确保民工听设计师的领导,而设计师本身也不干活,光是下命令,这里砌一堵墙,这里砌一扇门,这样民工开始建设,最后,我可以向民工要房子了。在这个过程中,设计师是什么也没有,除了他在脑子里的设计和命令,所以要房子也是跟民工要,记住了!


//最终产品为房子
class Room {
    var window: String  //窗户
    var floor: String  //地板
    
    init(window: String, floor: String) {
        self.window = window
        self.floor = floor
    }
}
//创建者
protocol Builder{
    //制作窗户
    func makeWindow()
    //制作地板
    func makeFloor()
    //房子建成
    func getRoom() ->Room?
}

//设计师其实就是指导者
class Designer{
    func commandOrder(builder: Builder){
        builder.makeWindow()
        builder.makeFloor()
    }
}

//具体创建者
class MinGong: Builder {
    var window: String?  //窗户
    var floor: String?  //地板
    
    func makeWindow() {
        window = "window"
    }
    func makeFloor() {
        floor = "floor"
    }
    func getRoom() -> Room?{
        if window == "window" && floor == "floor" {
            let room = Room(window: window!, floor: floor!)
            return room
        }
        return nil
    }
}
使用如下:

  //找到民工
  let minGong = MinGong()
  //找到设计师
  let designer = Designer()
  //民工遵守设计师的命令制作房子
  designer.commandOrder(builder: minGong)
  //房子建成
  if  let room = minGong.getRoom(){
      print("window: \(room.window), floor: \(room.floor)")
      //window: window, floor: floor
  }

KFC吃汉堡包

声明汉堡包

// MARK: - Product
public struct Hamburger {
    public let meat: Meat
    public let sauce: Sauces
    public let toppings: Toppings
}

extension Hamburger: CustomStringConvertible {
    public var description: String {
        return meat.rawValue + " burger"
    }
}
相关配菜

// 肉类
public enum Meat: String {
    case beef
    case chicken
    case kitten
    case tofu
}

// 酱汁
public struct Sauces: OptionSet {
    public static let mayonnaise = Sauces(rawValue: 1 << 0)
    public static let mustard = Sauces(rawValue: 1 << 1)
    public static let ketchup = Sauces(rawValue: 1 << 2)
    public static let secret = Sauces(rawValue: 1 << 3)

    public let rawValue: Int
    public init(rawValue: Int) {
        self.rawValue = rawValue
    }
}

// 浇头
public struct Toppings: OptionSet {
    public static let cheese = Toppings(rawValue: 1 << 0)
    public static let lettuce = Toppings(rawValue: 1 << 1)
    public static let pickles = Toppings(rawValue: 1 << 2)
    public static let tomatoes = Toppings(rawValue: 1 << 3)

    public let rawValue: Int
    public init(rawValue: Int) {
        self.rawValue = rawValue
    }
}
制作汉堡包

// MARK: - Builder
public class HambugerBuilder {

    public private(set) var meat: Meat = .beef
    public private(set) var sauces: Sauces = []
    public private(set) var toppings: Toppings = []

    private var soldOutMeats: [Meat] = [.kitten]

    public func addSauces(_ sauce: Sauces) {
        sauces.insert(sauce)
    }

    public func removeSauces(_ sauce: Sauces) {
        sauces.remove(sauce)
    }

    public func addToppings(_ topping: Toppings) {
        toppings.insert(topping)
    }

    public func removeToppgins(_ topping: Toppings) {
        toppings.remove(topping)
    }

    public func setMeat(_ meat: Meat) throws {
        guard isAvailable(meat) else {
            throw Error.soldOut
        }
        self.meat = meat
    }

    public func isAvailable(_ meat: Meat) -> Bool {
        return !soldOutMeats.contains(meat)
    }

    // 创建方式
    public func build() -> Hamburger {
        return Hamburger(meat: meat, sauce: sauces, toppings: toppings)
    }

    public enum Error: Swift.Error {
        case soldOut
    }
}
点单

// MARK: - Director
// 由员工来点单需要什么类型的汉堡包
public class Employee {
    public func createCombo1() throws -> Hamburger {
        let builder = HambugerBuilder()
        try builder.setMeat(.beef)
        builder.addSauces(.secret)
        builder.addToppings([.lettuce, .tomatoes, .pickles])
        return builder.build()
    }

    public func createKittenSpecial() throws -> Hamburger {
        let builder = HambugerBuilder()
        try builder.setMeat(.kitten)
        builder.addSauces(.mustard)
        builder.addToppings([.lettuce, .tomatoes])
        return builder.build()
    }
}
使用

// MARK: - Example
let burgerFipper = Employee()

if let combo1 = try? burgerFipper.createCombo1() {
    print(combo1.description)
}

if let kittenBurger = try? burgerFipper.createKittenSpecial() {
    print(kittenBurger.description)
} else {
    print("Sorry, no kitten burgers here...")
}
iOS中的使用场景

1、使用builder创建对象

protocol ThemeProtocol {
    var backgroundColor: UIColor? { get }
    var textColor: UIColor? { get }
}

class Theme: ThemeProtocol {
    var backgroundColor: UIColor?
    var textColor: UIColor?
    typealias buildThemeClosure = (Theme) -> Void

    init(build: buildThemeClosure) {
        build(self)
    }
}

let darkTheme = Theme(build: {
    $0.backgroundColor = UIColor.black
    $0.textColor = UIColor.white
})
let lightTheme = Theme(build: {
    $0.backgroundColor = UIColor.white
    $0.textColor = UIColor.black
})

什么时候使用

1:需要创建涉及各种部件的复杂对象,即需要一系列创建的步骤
2:当构造过程需要以不同的方式构建对象

3:相同的方法,不同的执行顺序,产生不同的事件结果


模式优点

1:松散耦合

生成器模式可以用同一个构建算法构建出表现上完全不同的产品,实现产品构建和产品表现上的分离。生成器模式正是把产品构建的过程独立出来,使它和具体产品的表现分松散耦合,从而使得构建算法可以复用,而具体产品表现也可以很灵活地、方便地展和切换。

2:可以很容易的改变产品的内部表示

在生成器模式中,由于Builder对象只是提供接口给Director使用,那么具体部件创建和装配方式是被Builder接口隐藏了的,Director并不知道这些具体的实现细节。这样一来,要想改变产品的内部表示,只需要切换Builder接口的具体实现即可,不用管Director,因此变得很容易。

3:更好的复用性

生成器模式很好的实现构建算法和具体产品实现的分离。这样一来,使得构建产品的算法可以复用。同样的道理,具体产品的实现也可以复用,同一个产品的实现,可以配合不同的构建算法使用。

模式缺点

1:建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,例如很多组成部分都不相同,不适合使用建造者模式,因此其使用范围受到一定的限制。

2:如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大,增加系统的理解难度和运行成本。


相关模式对比

抽象工厂模式与生成器相似,因为它也可以创建复杂对象。主要的区别是生成器模式着重于一步步构造一个复杂对象。而抽象工厂模式着重于多个系列的产品对象(简单的或是复杂的)。生成器在最后的一步返回产品,而对于抽象工厂来说,产品是立即返回的.


参考:




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值