創(chuàng)建者模式-對(duì)象模版模式(The Object Template Pattern)

本文大部分內(nèi)容翻譯至《Pro Design Pattern In Swift》By Adam Freeman鲁沥,一些地方做了些許修改祝沸,并將代碼升級(jí)到了Swift2.0,翻譯不當(dāng)之處望多包涵笙什。

對(duì)象模版模式

這里我們將介紹一個(gè)對(duì)于面向?qū)ο缶幊虂碚f非撑骞龋基礎(chǔ)的技術(shù)以至于不把它歸類到設(shè)計(jì)模式里面-用類或結(jié)構(gòu)體創(chuàng)建新對(duì)象屈嗤。


Swift中的元組是一系列的值組合在一起倦淀,用起來也很方便和簡(jiǎn)單,但是它們卻有一些限制卒暂。下面我們創(chuàng)建Command Line Tool 工程啄栓,請(qǐng)看main.swift文件。

main.swift

import Foundation

var products = [
    ("Kayak", "A boat for one person", 275.0, 10),
    ("Lifejacket", "Protective and fashionable", 48.95, 14),
    ("Soccer Ball", "FIFA-approved size and weight", 19.5, 32)
]

func calculateTax(product:(String, String, Double, Int)) -> Double {
    return product.2 * 0.2
}

func calculateStockValue(tuples:[(String, String, Double, Int)]) -> Double {
        return tuples.reduce(0, combine: {
        (total, product) -> Double in
        return total + (product.2 * Double(product.3))
        })
}

print("Sales tax for Kayak: $\(calculateTax(products[0]))")
print("Total value of stock: $\(calculateStockValue(products))")

在上面的代碼中也祠,我們定義了一個(gè)元素是元祖類型的數(shù)組代表產(chǎn)品以及兩個(gè)方法來操作它們昙楚。calculateTax方法接受一個(gè)元祖類型的參數(shù)用來計(jì)算價(jià)格的消費(fèi)稅,calculateStockValue方法對(duì)數(shù)組中所有的產(chǎn)品進(jìn)行總價(jià)計(jì)算诈嘿。執(zhí)行代碼堪旧,我們可以看見:

Sales tax for Kayak: $55.0
Total value of stock: $4059.3

設(shè)計(jì)模式要解決的問題大部分都是組件之間的緊密耦合。當(dāng)一個(gè)在一個(gè)組件的內(nèi)部去操作另一個(gè)組件時(shí)會(huì)產(chǎn)生緊密的耦合奖亚,或者淳梦,換一種說法,當(dāng)你想變更一個(gè)組件而不需要去升級(jí)另一個(gè)組件才是設(shè)計(jì)模式所推崇和解決的昔字。請(qǐng)看下圖:



兩個(gè)方法都跟元祖緊密的耦合在一起爆袍,不僅是它們定義參數(shù)的方式還是方法的內(nèi)容。 當(dāng)用元祖做參數(shù)的時(shí)候作郭,元祖的大小陨囊,順序,類型都必須完全匹配夹攒。在方法中蜘醋,又用元祖的下標(biāo)來獲取值,這就導(dǎo)致更緊密的耦合咏尝。
下面你將看見當(dāng)我刪掉元祖中一個(gè)值會(huì)發(fā)生什么:

import Foundation

var products = [
    ("Kayak", 275.0, 10),
    ("Lifejacket",  48.95, 14),
    ("Soccer Ball",  19.5, 32)
]

func calculateTax(product:(String, String, Double, Int)) -> Double {
    return product.2 * 0.2
}

func calculateStockValue(tuples:[(String, String, Double, Int)]) -> Double {
        return tuples.reduce(0, combine: {
        (total, product) -> Double in
        return total + (product.2 * Double(product.3))
        })
}

print("Sales tax for Kayak: $\(calculateTax(products[0]))")
print("Total value of stock: $\(calculateStockValue(products))")

我們刪掉了元祖中關(guān)于產(chǎn)品描述的值压语,但是很顯然我們現(xiàn)在跟著修改這兩個(gè)方法。



理解對(duì)象模版模式

對(duì)象模版模式利用一個(gè)類或者是結(jié)構(gòu)體來定義一個(gè)能夠創(chuàng)建對(duì)象的模版编检。當(dāng)組件需要一個(gè)對(duì)象時(shí)无蜂,它會(huì)通過指定模版的名稱來請(qǐng)求Swift運(yùn)行環(huán)境創(chuàng)建并且運(yùn)行環(huán)境會(huì)根據(jù)需要來初始化這個(gè)對(duì)象。



第一步就是組件提供模版名稱和一些要求的運(yùn)行時(shí)數(shù)據(jù)請(qǐng)求Swift運(yùn)行環(huán)境創(chuàng)建對(duì)象蒙谓。 第二步,Swift運(yùn)行環(huán)境會(huì)給要求的對(duì)象分配內(nèi)存然后用模版來創(chuàng)建它训桶,模版包含了用來設(shè)置對(duì)象的初始化狀態(tài)的初始化方法累驮。最后一步Swift運(yùn)行環(huán)境將創(chuàng)建好的對(duì)象交給請(qǐng)求的組件。


實(shí)現(xiàn)對(duì)象模版模式

Product.swift

import Foundation

class Product {
    var name:String
    var price:Double
    var stock:Int
    
    init(name:String, description:String, price:Double, stock:Int) {
        self.name = name
        self.description = description
        self.price = price
        self.stock = stock
    }
}

使用對(duì)象模版模式的好處

  • 解耦

我們將代碼做如下修改:

Product.swift

import Foundation

class Product {
    var name:String
    var description:String
    var price:Double
    var stock:Int
    
    init(name:String, price:Double, stock:Int) {
        self.name = name
        self.price = price
        self.stock = stock
    }
}

main.swift

import Foundation

var products = [
    Product(name: "Kayak", price: 275, stock: 10),
    Product(name: "Lifejacket", price: 48.95, stock: 14),
    Product(name: "Soccer Ball", price: 19.5, stock: 32)
]

func calculateTax(product:Product) -> Double {
    return product.price * 0.2;
}

func calculateStockValue(productsArray:[Product]) -> Double {
    return productsArray.reduce(0, combine: {(total, product) -> Double in
    return total + (product.price * Double(product.stock))
    })
}

我們更新了Product類將description屬性刪除了舵揭。最值得注意的是盡管我們修改了Product類谤专,卻對(duì)calculateTax方法和calculateStockValue方法沒有任何影響。這是因?yàn)樵赑roduct類中每一個(gè)屬性都是獨(dú)立定義的并且這兩個(gè)方法也沒有操作description屬性午绳。

  • 封裝

用類或者結(jié)構(gòu)體來定義數(shù)據(jù)模版最大的好處就是封裝置侍。封裝允許數(shù)值和操作這些數(shù)值的邏輯以一個(gè)簡(jiǎn)單的方式聯(lián)系起來。將數(shù)值和邏輯聯(lián)系起來的話使得代碼更有可讀性。

Product.swift

import Foundation

class Product {
    var name:String
    var price:Double
    var stock:Int
    
    var stockValue:Double{
        
        return self.price * Double(self.stock)
    }
    
    init(name:String, price:Double, stock:Int) {
        self.name = name
        self.price = price
        self.stock = stock
    }

   func calculateTax(rate: Double) -> Double { 
       return self.price * rate
    }
}

main.swift

...

func calculateStockValue(productsArray:[Product]) -> Double {
    return productsArray.reduce(0, combine: {(total, product) -> Double in
    return total + product.stockValue
    })
}

...

這看著也許是簡(jiǎn)單的修改蜡坊,但是重要的事情發(fā)生了:Product類現(xiàn)在有了公有描述(public presentation)和私有實(shí)現(xiàn)(private implementation)杠输。



公有描述就是其他組件能夠使用的API。任何組件都能設(shè)置和獲取name秕衙,price和stock屬性的值蠢甲。公有描述也包括stockValue屬性和calculateTax方法,但重要的是注意并不是指它們的實(shí)現(xiàn)据忘。
阻止一個(gè)屬性或者一個(gè)方法暴露它具體實(shí)現(xiàn)能夠很容易的打破耦合鹦牛,因?yàn)檫@樣就使得其他組件的依賴變成不可能。例如我們將Proudct類中的calculateTax方法做下面的修改:

...
  func calculateTax(rate: Double) -> Double {
         return min(10, self.price * rate)
  }
...

因?yàn)樾薷牡膬?nèi)容是在Product類中勇吊,這個(gè)修改對(duì)于其他的組件來說是不可見的曼追,所以可以看出其他組件不會(huì)對(duì)Product類的實(shí)現(xiàn)產(chǎn)生依賴。


  • 進(jìn)化的公有描述

Swift 中很重要的一個(gè)特型就是可以隨著應(yīng)用的改變你也可以進(jìn)化類的公有描述汉规。就目前來看礼殊,stock屬性是一個(gè)可以設(shè)置任何Int類型值的存儲(chǔ)屬性,但是對(duì)于庫存來說負(fù)數(shù)是沒有任何意義的鲫忍。Swift支持我們能夠無縫的將存儲(chǔ)屬性修改成計(jì)算屬性膏燕。

import Foundation
class Product {
    var name:String
    var price:Double
    
    private var stockBackingValue:Int = 0
    
    var stock:Int {
        get {
              return stockBackingValue
        }
        
        set {
             stockBackingValue = max(0, newValue)
        }
    }
    
    var stockValue:Double{
        
        return self.price * Double(self.stock)
    }
    
    init(name:String, price:Double, stock:Int) {
        self.name = name
        self.price = price
        self.stock = stock
    }
    
    func calculateTax(rate: Double) -> Double {
        return min(10,self.price * rate)
    }
}

我們定義了一個(gè) stockBackingValue存儲(chǔ)屬性的變量來儲(chǔ)存計(jì)算屬性變量stock的值,stock的get方法僅僅是簡(jiǎn)單的返回stockBackingValue的值悟民,但是set方法去用了max方法來保證當(dāng)設(shè)定值是負(fù)數(shù)的時(shí)候用0來代替坝辫。



現(xiàn)在修改main.swift如下

main.swift

import Foundation

var products = [
    Product(name: "Kayak", price: 275, stock: 10),
    Product(name: "Lifejacket", price: 48.95, stock: 14),
    Product(name: "Soccer Ball", price: 19.5, stock: 32)
]

func calculateTax(product:Product) -> Double {
    return product.price * 0.2;
}

func calculateStockValue(productsArray:[Product]) -> Double {
    return productsArray.reduce(0, combine: {(total, product) -> Double in
    return total + product.stockValue
    })
}
print("Sales tax for Kayak: $\(products[0].calculateTax(0.2))")
print("Total value of stock: $\(calculateStockValue(products))")
products[0].stock = -50
print("Stock Level for Kayak: \(products[0].stock)")

運(yùn)行程序,得到下面結(jié)果:

Sales tax for Kayak: $10.0
Total value of stock: $4059.3
Stock Level for Kayak: 0

理解對(duì)象模版模式的陷阱

對(duì)象模版模式需要注意的陷阱就是模版的類型選擇射亏,好比當(dāng)用類更合適的時(shí)候卻使用了結(jié)構(gòu)體近忙。Swift的類和結(jié)構(gòu)體有很多共性,但尤其注意的是結(jié)構(gòu)體是值類型智润,類是引用類型及舍。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市窟绷,隨后出現(xiàn)的幾起案子锯玛,更是在濱河造成了極大的恐慌,老刑警劉巖兼蜈,帶你破解...
    沈念sama閱讀 210,978評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件攘残,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡为狸,警方通過查閱死者的電腦和手機(jī)歼郭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來辐棒,“玉大人病曾,你說我怎么就攤上這事牍蜂。” “怎么了泰涂?”我有些...
    開封第一講書人閱讀 156,623評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵鲫竞,是天一觀的道長。 經(jīng)常有香客問我负敏,道長贡茅,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,324評(píng)論 1 282
  • 正文 為了忘掉前任其做,我火速辦了婚禮顶考,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘妖泄。我一直安慰自己驹沿,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,390評(píng)論 5 384
  • 文/花漫 我一把揭開白布蹈胡。 她就那樣靜靜地躺著渊季,像睡著了一般。 火紅的嫁衣襯著肌膚如雪罚渐。 梳的紋絲不亂的頭發(fā)上却汉,一...
    開封第一講書人閱讀 49,741評(píng)論 1 289
  • 那天,我揣著相機(jī)與錄音荷并,去河邊找鬼合砂。 笑死,一個(gè)胖子當(dāng)著我的面吹牛源织,可吹牛的內(nèi)容都是我干的翩伪。 我是一名探鬼主播,決...
    沈念sama閱讀 38,892評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼谈息,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼缘屹!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起侠仇,我...
    開封第一講書人閱讀 37,655評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤轻姿,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后逻炊,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體互亮,經(jīng)...
    沈念sama閱讀 44,104評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評(píng)論 2 325
  • 正文 我和宋清朗相戀三年嗅骄,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片饼疙。...
    茶點(diǎn)故事閱讀 38,569評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡溺森,死狀恐怖慕爬,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情屏积,我是刑警寧澤医窿,帶...
    沈念sama閱讀 34,254評(píng)論 4 328
  • 正文 年R本政府宣布,位于F島的核電站炊林,受9級(jí)特大地震影響姥卢,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜渣聚,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,834評(píng)論 3 312
  • 文/蒙蒙 一独榴、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧奕枝,春花似錦棺榔、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,725評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至谭梗,卻和暖如春忘晤,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背激捏。 一陣腳步聲響...
    開封第一講書人閱讀 31,950評(píng)論 1 264
  • 我被黑心中介騙來泰國打工设塔, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人缩幸。 一個(gè)月前我還...
    沈念sama閱讀 46,260評(píng)論 2 360
  • 正文 我出身青樓壹置,卻偏偏與公主長得像,于是被迫代替她去往敵國和親表谊。 傳聞我的和親對(duì)象是個(gè)殘疾皇子钞护,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,446評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容

  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫、插件爆办、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 12,058評(píng)論 4 62
  • 那條路就在你的身后难咕,你可以前行或往回走,挽留的話距辆,我沒有說出口余佃,你的年齡早已熟透,也許你的字典里沒有什么人或...
    懂我笑容任閱讀 204評(píng)論 0 0
  • 最近做的項(xiàng)目使用了NSThread來開啟一個(gè)線程來保持與設(shè)備的連接跨算,然而當(dāng)手機(jī)與設(shè)備斷開連接且需要釋放掉該線程的時(shí)...
    行者之心閱讀 427評(píng)論 0 0
  • 你是否也會(huì)遇到這樣的窘境: 明明心中很想奮斗爆土,清單上列了一堆想做的事情和學(xué)習(xí)的技能,細(xì)細(xì)想來卻發(fā)現(xiàn)自己什么都沒有完...
    正記錄Beta閱讀 4,999評(píng)論 6 25
  • 最近沒有太多讓人值得高興的事情 生日也就簡(jiǎn)單過一次 24 歲好像知道身上責(zé)任有多重 真的是一天一天就變老了
    RayThinking閱讀 197評(píng)論 0 0