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

本文大部分内容翻译至《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
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 201,924评论 5 474
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,781评论 2 378
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,813评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,264评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,273评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,383评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,800评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,482评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,673评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,497评论 2 318
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,545评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,240评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,802评论 3 304
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,866评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,101评论 1 258
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,673评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,245评论 2 341

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,573评论 18 139
  • 没有人买车会只买一个轮胎或者方向盘,大家买的都是一辆包含轮胎、方向盘和发动机等多个部件的完整汽车。如何将这些部件组...
    justCode_阅读 1,816评论 1 6
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 11,994评论 4 61
  • 在公园散步 在树下谈心 在商场闲逛 手拉着手 喜欢你骑车载我回家 搂着你的腰 这画面曾经是我羡慕的 羡慕着别人一起...
    追求快乐阅读 296评论 0 1
  • 在以前的博文中谈了自己对用户成熟度的一些看法以及不成熟用户的一些表现。最近回了一趟老家,再度让我思考用户成熟度的问...
    马虎眼阅读 1,000评论 0 19