本文大部分內(nèi)容翻譯至《Pro Design Pattern In Swift》By Adam Freeman,一些地方做了些許修改耿眉,并將代碼升級(jí)到了Swift2.0,翻譯不當(dāng)之處望多包涵钳榨。
建造者模式(The Builder Pattern)
建造者模式用來(lái)將對(duì)象的配置從創(chuàng)建中分離出來(lái)列粪。請(qǐng)求組件有配置數(shù)據(jù)并將它傳遞給中間人-建造者-它負(fù)責(zé)創(chuàng)建代表組件的對(duì)象薇宠。建造者模式它可以將復(fù)雜對(duì)象的建造過(guò)程抽象出來(lái)(抽象類別)肖油,使這個(gè)抽象過(guò)程的不同實(shí)現(xiàn)方法可以構(gòu)造出不同表現(xiàn)(屬性)的對(duì)象憨愉。
示例工程
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)")
}
}
接下來(lái)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()
運(yùn)行程序:
Name Joe
Veggie: false
Patties: 2
Pickles: true
Mayo: true
Ketchup: true
Lettuce: true
Cook: Normal
理解建造者模式解決的問(wèn)題
當(dāng)一個(gè)對(duì)象需要大量的配置數(shù)據(jù)的時(shí)候烦绳,建造者模式出現(xiàn)了。在上面的例子中莱衩,Burger類的初始化方法就要求每一個(gè)方面的配置數(shù)據(jù)爵嗅。下面的是虛構(gòu)的餐館預(yù)定漢堡包過(guò)程:
- 服務(wù)員問(wèn)顧客的姓名
- 服務(wù)員問(wèn)顧客是否需要素食
- 服務(wù)員問(wèn)顧客是否要定做漢堡包
- 服務(wù)員問(wèn)顧客是否要升級(jí)買(mǎi)一個(gè)額外的肉餅
上面只有4個(gè)步驟,但卻拋出了一些問(wèn)題笨蚁。其實(shí)我們?cè)趧?chuàng)建Burger對(duì)象的時(shí)候是這樣的:
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類的初始化方法要求請(qǐng)求組件知道默認(rèn)的值當(dāng)顧客不想改變Burger的配置的時(shí)候睹晒。每一個(gè)請(qǐng)求組件都必須知道這個(gè)事情趟庄,那就意味著如果改變一個(gè)默認(rèn)的值,那么就必須改變每一個(gè)請(qǐng)求組件伪很。
理解建造者模式
建造者模式解決這個(gè)問(wèn)題通過(guò)引出一個(gè)中間人-建造者-在請(qǐng)求組件和需要?jiǎng)?chuàng)建的對(duì)象之間戚啥。
實(shí)現(xiàn)建造者模式
1. 定義創(chuàng)建者類
首先,就是創(chuàng)建建造者類锉试,建造者類提供了Burger類參數(shù)的默認(rèn)值并且允許請(qǐng)求組件去改變這些值猫十。
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()
運(yùn)行程序,輸出:
Name Joe
Veggie: false
Patties: 2
Pickles: true
Mayo: false
Ketchup: true
Lettuce: true
Cook: Well Done
建造者模式的變形
你可以將建造者模式和其他模式結(jié)合起來(lái)呆盖,一般是和工廠方法模式和抽象工廠模式拖云。用得最多的就是定義復(fù)數(shù)的建造者并且讓它們都實(shí)現(xiàn)工廠方法模式。
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()
運(yùn)行程序应又,輸出:
Name Joe
Veggie: false
Patties: 4
Pickles: true
Mayo: false
Ketchup: true
Lettuce: true
Cook: Well Done
Cocoa中的建造者模式
Cocoa中最常用的使用的建造者模式的類就是 NSDateComponents類宙项,NSDateComponents類允許請(qǐng)求組件設(shè)置值來(lái)創(chuàng)建 NSDate類。請(qǐng)看下面例子:
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!)
運(yùn)行程序株扛,輸出:
1940-09-06 01:00:00 +0000