×

创建者模式-建造者模式(The Builder Pattern)

96
ZhouMac
2016.03.22 20:57* 字数 737

本文大部分内容翻译至《Pro Design Pattern In Swift》By Adam Freeman,一些地方做了些许修改,并将代码升级到了Swift2.0,翻译不当之处望多包涵。

建造者模式(The Builder Pattern)

建造者模式用来将对象的配置从创建中分离出来。请求组件有配置数据并将它传递给中间人-建造者-它负责创建代表组件的对象。建造者模式它可以将复杂对象的建造过程抽象出来(抽象类别),使这个抽象过程的不同实现方法可以构造出不同表现(属性)的对象。


示例工程

OS X Command Line Tool工程:

Food.swift

class Burger {
    
    let customerName:String
    let veggieProduct:Bool
    let patties:Int
    let pickles:Bool
    let mayo:Bool
    let ketchup:Bool
    let lettuce:Bool
    let cook:Cooked
    
    enum Cooked : String {
        case RARE = "Rare"
        case NORMAL = "Normal"
        case WELLDONE = "Well Done"
    }
    
    init(name:String, veggie:Bool, patties:Int, pickles:Bool, mayo:Bool,
        ketchup:Bool, lettuce:Bool, cook:Cooked) {
        self.customerName = name
        self.veggieProduct = veggie
        self.patties = patties
        self.pickles = pickles
        self.mayo = mayo
        self.ketchup = ketchup
        self.lettuce = lettuce
        self.cook = cook
    }
    
    func printDescription() {
        print("Name \(self.customerName)")
        print("Veggie: \(self.veggieProduct)")
        print("Patties: \(self.patties)")
        print("Pickles: \(self.pickles)")
        print("Mayo: \(self.mayo)")
        print("Ketchup: \(self.ketchup)")
        print("Lettuce: \(self.lettuce)")
        print("Cook: \(self.cook.rawValue)")
    }
}

接下来main.swift

main.swift

let order = Burger(name: "Joe", veggie: false, patties: 2, pickles: true,
    mayo: true, ketchup: true, lettuce: true, cook: Burger.Cooked.NORMAL)
order.printDescription()

运行程序:

Name Joe
Veggie: false
Patties: 2
Pickles: true
Mayo: true
Ketchup: true
Lettuce: true
Cook: Normal

理解建造者模式解决的问题

当一个对象需要大量的配置数据的时候,建造者模式出现了。在上面的例子中,Burger类的初始化方法就要求每一个方面的配置数据。下面的是虚构的餐馆预定汉堡包过程:

  1. 服务员问顾客的姓名
  2. 服务员问顾客是否需要素食
  3. 服务员问顾客是否要定做汉堡包
  4. 服务员问顾客是否要升级买一个额外的肉饼

上面只有4个步骤,但却抛出了一些问题。其实我们在创建Burger对象的时候是这样的:

main.swift

// Step 1 - Ask for name
let name = "Joe"
// Step 2 - Is veggie meal required?
let veggie = false
// Step 3 - Customize burger?
let pickles = true
let mayo = false
let ketchup = true
let lettuce = true
// Step 4 - Buy additional patty?
let patties = 2
let cooked = Burger.Cooked.NORMAL

let order = Burger(name: name, veggie: veggie, patties: patties, pickles: pickles, mayo: mayo, ketchup: ketchup, lettuce: lettuce, cook: cooked)
order.printDescription()

Burger类的初始化方法要求请求组件知道默认的值当顾客不想改变Burger的配置的时候。每一个请求组件都必须知道这个事情,那就意味着如果改变一个默认的值,那么就必须改变每一个请求组件。


理解建造者模式

建造者模式解决这个问题通过引出一个中间人-建造者-在请求组件和需要创建的对象之间。



实现建造者模式

1. 定义创建者类

首先,就是创建建造者类,建造者类提供了Burger类参数的默认值并且允许请求组件去改变这些值。

Builder.swift

class BurgerBuilder {
    private var veggie  = false
    private var pickles = true
    private var mayo    = true
    private var ketchup = true
    private var lettuce = true
    private var cooked  = Burger.Cooked.NORMAL
    private var patties = 2
    
    func setVeggie(choice: Bool)  { self.veggie  = choice }
    func setPickles(choice: Bool) { self.pickles = choice }
    func setMayo(choice: Bool)    { self.mayo    = choice }
    func setKetchup(choice: Bool) { self.ketchup = choice }
    func setLettuce(choice: Bool) { self.lettuce = choice }
    func setCooked(choice: Burger.Cooked) { self.cooked = choice }
    func addPatty(choice: Bool)   { self.patties = choice ? 3 : 2 }
    
    func buildObject() -> Burger{
        return Burger(name: name, veggie: veggie, patties: patties, pickles: pickles, mayo: mayo, ketchup: ketchup, lettuce: lettuce, cook: cooked)
    }
    
}

2. 使用建造者

main.swift

var builder = BurgerBuilder()
// Step 1 - Ask for name
let name = "Joe"
// Step 2 - Is veggie meal required?
builder.setVeggie(false)
// Step 3 - Customize burger?
builder.setMayo(false)
builder.setCooked(Burger.Cooked.WELLDONE)
// Step 4 - Buy additional patty?
builder.addPatty(false)
let order = builder.buildObject(name)
order.printDescription()

运行程序,输出:

Name Joe
Veggie: false
Patties: 2
Pickles: true
Mayo: false
Ketchup: true
Lettuce: true
Cook: Well Done

建造者模式的变形

你可以将建造者模式和其他模式结合起来,一般是和工厂方法模式和抽象工厂模式。用得最多的就是定义复数的建造者并且让它们都实现工厂方法模式。

Builder.swift

enum Burgers {
    case STANDARD
    case BIGBURGER
    case SUPERVEGGIE
}

class BurgerBuilder {
    private var veggie  = false
    private var pickles = true
    private var mayo    = true
    private var ketchup = true
    private var lettuce = true
    private var cooked  = Burger.Cooked.NORMAL
    private var patties = 2
    private var bacon   = true
    
    private init() {
        // do nothing
    }
    
    func setVeggie(choice: Bool)  {
        self.veggie = choice
        if (choice) {
            self.bacon = false
        }
    }
    func setPickles(choice: Bool) { self.pickles = choice }
    func setMayo(choice: Bool)    { self.mayo    = choice }
    func setKetchup(choice: Bool) { self.ketchup = choice }
    func setLettuce(choice: Bool) { self.lettuce = choice }
    func setCooked(choice: Burger.Cooked) { self.cooked = choice }
    func addPatty(choice: Bool)   { self.patties = choice ? 3 : 2 }
    func setBacon(choice: Bool)   { self.bacon   = choice }
    
    func buildObject(name:String) -> Burger{
        return Burger(name: name, veggie: veggie, patties: patties, pickles: pickles, mayo: mayo, ketchup: ketchup, lettuce: lettuce, cook: cooked,bacon: bacon)
    }
    
    class func getBuilder(burgerType:Burgers) -> BurgerBuilder {
        var builder:BurgerBuilder
        switch (burgerType) {
            case .BIGBURGER: builder   = BigBurgerBuilder()
            case .SUPERVEGGIE: builder = SuperVeggieBurgerBuilder()
            case .STANDARD: builder    = BurgerBuilder()
        }
        return builder
    }
    
}

class BigBurgerBuilder : BurgerBuilder {
    private override init() {
        super.init()
        self.patties = 4
        self.bacon = false
    }
        
    override func addPatty(choice: Bool) {
        fatalError("Cannot add patty to Big Burger")
    }
}

class SuperVeggieBurgerBuilder : BurgerBuilder {
    private override init() {
        super.init()
        self.veggie = true
        self.bacon = false
    }
        
    override func setVeggie(choice: Bool) {
        // do nothing - always veggie
    }
        
    override func setBacon(choice: Bool) {
        fatalError("Cannot add bacon to this burger")
    }
}

接着是:

main.swift

// Step 1 - Ask for name
let name = "Joe"

// Step 2 - Select a Product
let builder = BurgerBuilder.getBuilder(Burgers.BIGBURGER)

// Step 3 - Customize burger?
builder.setMayo(false)
builder.setCooked(Burger.Cooked.WELLDONE)

let order = builder.buildObject(name)
order.printDescription()

运行程序,输出:

Name Joe
Veggie: false
Patties: 4
Pickles: true
Mayo: false
Ketchup: true
Lettuce: true
Cook: Well Done

Cocoa中的建造者模式

Cocoa中最常用的使用的建造者模式的类就是 NSDateComponents类,NSDateComponents类允许请求组件设置值来创建 NSDate类。请看下面例子:

import Foundation
var builder = NSDateComponents()

builder.hour = 10
builder.day = 6
builder.month = 9
builder.year = 1940
builder.calendar = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)

var date = builder.date
print(date!)

运行程序,输出:

1940-09-06 01:00:00 +0000
Pro Design Pattern In Swift
Web note ad 1