提問(wèn):是否理解為什么緊密耦合是一個(gè)很大的問(wèn)題街图?
// 產(chǎn)品
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)]
// 費(fèi)率
func calculateTax(product:(String, String, Double, Int)) -> Double {
return product.2 * 0.2;
}
// 股票的價(jià)值
func calculateStockValue(tuples:[(String, String, Double, Int)]) -> Double {
return tuples.reduce(0, {
(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))");
- 緊密耦合的組件會(huì)讓代碼難以維護(hù),比如上面的代碼如果需要改變產(chǎn)品就會(huì)變得很麻煩懒构,所以必須要進(jìn)行更改餐济,并測(cè)試它們的影響。在一個(gè)組件中的改變要求改變那些依賴其執(zhí)行胆剧。如果在應(yīng)用程序中包含大量的緊耦合絮姆,那么我們可以通過(guò)修改代碼醉冤,級(jí)聯(lián)和制作一個(gè)簡(jiǎn)單的修正或添加新功能的行為來(lái)避免成為大量的重寫(xiě)伏筆,松散耦合的組件在設(shè)計(jì)模式中的一個(gè)關(guān)鍵目標(biāo)篙悯。
我刪除了描述產(chǎn)品的價(jià)值和突出顯示的語(yǔ)句顯示所需要的功能的相應(yīng)變革蚁阳。在實(shí)際項(xiàng)目中,這些變化可以裝載鸽照,以及大量更改螺捐,如果它們影響到其他緊密耦合,然后可以導(dǎo)致代碼被修改的應(yīng)用程序中很大一部分矮燎。這一級(jí)別的變化很難管理定血,需要進(jìn)行全面的測(cè)試,以確保始終如一地應(yīng)用了更改诞外,所做的更改沒(méi)有引入任何新的 bug澜沟。
了解對(duì)象模板模式
對(duì)象模板模式使用類或結(jié)構(gòu)定義從中創(chuàng)建對(duì)象的模板。當(dāng)應(yīng)用程序組件需要一個(gè)對(duì)象時(shí)峡谊,它呼吁swift運(yùn)行時(shí)創(chuàng)建通過(guò)指定模板和配置對(duì)象所需的任何運(yùn)行時(shí)初始化數(shù)據(jù)值的名稱茫虽。
對(duì)象模板模式:
1.第一個(gè)操作是調(diào)用組件要求swift運(yùn)行時(shí)創(chuàng)建一個(gè)對(duì)象,提供要使用的模板和自定義將創(chuàng)建該對(duì)象所需的任何運(yùn)行時(shí)數(shù)據(jù)值的名稱靖苇。
2.第二次操作席噩,swift運(yùn)行時(shí)分配存儲(chǔ)該對(duì)象所需的內(nèi)存,并使用該模板來(lái)創(chuàng)建它贤壁。模板包含用于準(zhǔn)備對(duì)象設(shè)置所使用的初始狀態(tài)、 通過(guò)提供調(diào)用組件的運(yùn)行時(shí)值或模板 (或兩者) 中, 定義的值的初始值設(shè)定項(xiàng)方法和swift運(yùn)行時(shí)調(diào)用初始值設(shè)定項(xiàng)準(zhǔn)備使用的對(duì)象埠忘。在最后一個(gè)操作脾拆,swift運(yùn)行時(shí)將它已經(jīng)創(chuàng)建的對(duì)象給調(diào)用的組件。這三個(gè)步驟的過(guò)程莹妒,我們均可以一遍遍重復(fù)名船,單個(gè)模板可用于創(chuàng)建多個(gè)對(duì)象。
了解類結(jié)構(gòu)旨怠、 對(duì)象和實(shí)例
有些的面向?qū)ο缶幊绦g(shù)語(yǔ)中所使用松散的日常開(kāi)發(fā)但理解設(shè)計(jì)模式的時(shí)候渠驼,那可能會(huì)造成混淆。這種模式的關(guān)鍵條件是類鉴腻、結(jié)構(gòu)迷扇、對(duì)象和實(shí)例.
類和結(jié)構(gòu)都是這兩個(gè)模板,它們是swift如下對(duì)象模板模式的食譜爽哎。swift跟隨在模板中的說(shuō)明創(chuàng)建新對(duì)象蜓席。可以重復(fù)使用相同的模板來(lái)創(chuàng)建多個(gè)對(duì)象课锌。每個(gè)對(duì)象是不同的但它創(chuàng)建使用相同的指令厨内,就像一個(gè)食譜可以用于創(chuàng)建多個(gè)蛋糕 (添加一個(gè) Int,一種方法來(lái)更改它的值,等等)雏胃。
Word實(shí)例具有相同的含義為對(duì)象请毛,但它用來(lái)指用于所以創(chuàng)建該對(duì)象的圖案的名稱,產(chǎn)品也可以調(diào)用對(duì)象的一個(gè)實(shí)例產(chǎn)品班上
重要的一點(diǎn)是瞭亮,類和結(jié)構(gòu)是你寫(xiě)在開(kāi)發(fā)過(guò)程中的說(shuō)明進(jìn)行操作方仿,對(duì)象創(chuàng)建時(shí)應(yīng)用程序。當(dāng)您更改存儲(chǔ)一個(gè)對(duì)象的值時(shí)街州,例如兼丰,它不會(huì)更改用于創(chuàng)建它的圖案。
創(chuàng)建模板類(封裝一個(gè)類)
新建一個(gè)名字叫做 Product
的類
Product.swift File
的內(nèi)容
class Product {
var name:String;
var description: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;
}
}
上面的方法盡封裝了一個(gè)簡(jiǎn)單的類唆缴,接著我會(huì)加入功能鳍征,然后再原來(lái)的文件中進(jìn)行重構(gòu)賦值。
// 產(chǎn)品通過(guò)重寫(xiě)的一個(gè)產(chǎn)品類來(lái)進(jìn)行重新賦值
var products = [
Product(name: "Kayak", description: "A boat for one person",
price: 275, stock: 10),
Product(name: "Lifejacket", description: "Protective and fashionable",
price: 48.95, stock: 14),
Product(name: "Soccer Ball", description: "FIFA-approved size and weight",
price: 19.5, stock: 32)];
func calculateTax(product:Product) -> Double {
return product.price * 0.2;
}
func calculateStockValue(productsArray:[Product]) -> Double {
return productsArray.reduce(0, {(total, product) -> Double in
return total + (product.price * Double(product.stock))
}); }
print("Sales tax for Kayak: $\(calculateTax(products[0]))");
print("Total value of stock: $\(calculateStockValue(products))");
PS:封裝一個(gè)類面徽,其實(shí)就是通過(guò)定義一個(gè)模板需要的屬性來(lái)進(jìn)行工作艳丛,這樣有非常多的好處,在面對(duì)對(duì)象的編程使用類和結(jié)構(gòu)體是一個(gè)更快更直接的方法趟紊,使用模板時(shí)氮双,就有兩個(gè)步驟 ︰ 定義模板并創(chuàng)建對(duì)象使用的模板。
上面的代碼是非常簡(jiǎn)單的霎匈。它沒(méi)有功能戴差,只是封裝了類和結(jié)構(gòu),但它證明了即使是最簡(jiǎn)單的模板也可以減少變化的影響铛嘱。那么這個(gè)時(shí)候我們來(lái)刪除一些數(shù)據(jù)就非常方便了暖释。
從產(chǎn)品類中刪除屬性
class Product {
var name:String;
var price:Double;
var stock:Int;
init(name:String, price:Double, stock:Int) {
self.name = name;
self.price = price;
self.stock = stock;
} }
這里是刪除了詳情說(shuō)明
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, {
(total, product) -> Double in
return total + (product.price * Double(product.stock))
}); }
print("Sales tax for Kayak: $\(calculateTax(products[0]))");
print("Total value of stock: $\(calculateStockValue(products))");
現(xiàn)在的代碼失去了詳情的說(shuō)明。但是calculateTax 和 calculateStockValue 在所有墨吓,并且每個(gè)功能的因?yàn)槊總€(gè)類中的屬性是定義和訪問(wèn)獨(dú)立于其他屬性既不函數(shù)依賴于描述屬性球匕。
使用類和結(jié)構(gòu)限制只是直接受到影響的變化,防止使用不太結(jié)構(gòu)化的數(shù)據(jù)類型帖烘。
封裝的好處
從數(shù)據(jù)對(duì)象作為模板亮曹,使用類或結(jié)構(gòu)中最重要的好處是對(duì)封裝的支持。
封裝是面向?qū)ο缶幊痰暮诵乃枷胫幻刂ⅲ羞@種想法的軸承對(duì)這一章的兩個(gè)方面照卦。
第一個(gè)方面是,封裝允許的數(shù)據(jù)值和操作這些值將被結(jié)合到單個(gè)組件中的邏輯历极。結(jié)合數(shù)據(jù)和邏輯使得它易于閱讀的代碼窄瘟,因?yàn)橐磺杏嘘P(guān)的數(shù)據(jù)類型定義在相同的地方。現(xiàn)在要加上一些邏輯趟卸。
在 Product.swift File 中添加邏輯
class Product {
var name:String;
var price:Double;
var stock:Int;
init(name:String, price:Double, stock:Int) {
self.name = name;
self.price = price;
self.stock = stock;
}
//計(jì)算匯率的方法
func calculateTax(rate: Double) -> Double {
return self.price * rate;
}
//股票價(jià)值
var stockValue: Double {
get {
return self.price * Double(self.stock);
}
}
}
我增加了calculateTax方法蹄葱,它接受稅率作為參數(shù)氏义,用它來(lái)計(jì)算銷售稅,和stockValue計(jì)算得出的特性图云,實(shí)現(xiàn) getter 子句惯悠,計(jì)算該股票的總價(jià)值。
我更新的代碼語(yǔ)句 main.swift 文件和操作產(chǎn)品反對(duì)使用新的方法和屬性竣况,更新 main.swift 文件中的代碼
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 calculateStockValue(productsArray:[Product]) -> Double {
return productsArray.reduce(0, {(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))");
這些看似簡(jiǎn)單的變化克婶,但一些重要的事情發(fā)生了︰
產(chǎn)品類現(xiàn)在有public的演示文稿和私有的實(shí)現(xiàn)
產(chǎn)品類的public
和private
方面
public是其他組件可以使用的 API。任何組件可以獲取或設(shè)置的值名稱, 價(jià)格丹泉,和股票屬性在任何他們需要的方式中使用它們情萤。
public還包括 stockValue 屬性和 calculateTax 方法,但是 — — 這是重要的部分 — — 不是它們的實(shí)現(xiàn)摹恨。
提示不要混淆使用的私有實(shí)現(xiàn)想法private關(guān)鍵字筋岛。private關(guān)鍵字限制可以使用的類、 方法或?qū)傩陨购澹词巩?dāng)private不使用關(guān)鍵字睁宰、 實(shí)施方法和計(jì)算的屬性不可見(jiàn)到調(diào)用組件。
能力呈現(xiàn)屬性或方法寝凌,而又不暴露其執(zhí)行容易打破緊密耦合柒傻,因?yàn)樗遣豢赡艿牧硪唤M件將取決于執(zhí)行。作為一個(gè)例子较木,我是如何改變的執(zhí)行 calculateTax 方法來(lái)定義最大的稅額红符。因?yàn)樵趫?zhí)行執(zhí)行計(jì)算產(chǎn)品對(duì)象,變化是肉眼看不到其他元件伐债,因此相信產(chǎn)品類知道如何執(zhí)行其計(jì)算违孝。
改變?cè)?Product.swift File 中的方法實(shí)現(xiàn)
func calculateTax(rate: Double) -> Double {
return min(10, self.price * rate);
}
我用函數(shù)從swift的標(biāo)準(zhǔn)庫(kù),設(shè)置了銷售稅 10 美元的最高限量泳赋。我有只顯示 calculateTax 方法在清單,因?yàn)闆](méi)有其他代碼語(yǔ)句在playground上不得不改變喇喉,以適應(yīng)新的稅計(jì)算; 變化是在私有實(shí)現(xiàn)部分的產(chǎn)品類祖今,與其他組件都無(wú)法創(chuàng)建依賴項(xiàng)。運(yùn)行該應(yīng)用程序會(huì)產(chǎn)生以下結(jié)果 ︰
皮劃艇的銷售稅: $10.0
總價(jià)值的股票: $4059.3
擴(kuò)展public的好處
A 的 Swift 不錯(cuò)的功能是你可以進(jìn)化隨著時(shí)間流逝拣技,更改應(yīng)用程序類的public演示文稿的方式千诬。由此看來(lái), 股票屬性是一個(gè)標(biāo)準(zhǔn)的存儲(chǔ)的屬性膏斤,可以設(shè)置為任何 Int 值徐绑,但它沒(méi)有意義的股票,有負(fù)的項(xiàng)目數(shù)莫辨,這樣做會(huì)影響所返回的結(jié)果 stockValue 計(jì)算屬性傲茄。
swift允許我無(wú)縫地替代股票-存儲(chǔ)屬性計(jì)算的屬性毅访,其實(shí)現(xiàn)可以強(qiáng)制驗(yàn)證策略,以確保庫(kù)存水平是永遠(yuǎn)不會(huì)小于零盘榨。在 Product.swift File 中添加一個(gè)計(jì)算的屬性
class Product {
var name:String;
var price:Double;
private var stockBackingValue:Int = 0;
var stock:Int {
get {
return stockBackingValue;
}
set {
stockBackingValue = max(0, newValue);
} }
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);
}
var stockValue: Double {
get {
return self.price * Double(self.stock);
}
} }
我定義了一個(gè)支持變量將保存的值股票屬性和已經(jīng)取代了存儲(chǔ)股票具有計(jì)算具有屬性的屬性 getter 和 setter 的屬性喻粹。吸氣劑只是返回屬性的值的支持,我有被命名為 stockBackingValue草巡,但二傳手使用從標(biāo)準(zhǔn)庫(kù)將支持值設(shè)置為零守呜,當(dāng)負(fù)的值用來(lái)設(shè)置該屬性的最大功能。這種變化的影響的確是的public和private部分的產(chǎn)品類有改變山憨,但這不會(huì)影響代碼的方式查乒,使用的類
存儲(chǔ)的屬性更改為計(jì)算屬性的影響在 main.swift 文件中檢查驗(yàn)證
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 calculateStockValue(productsArray:[Product]) -> Double {
return productsArray.reduce(0, {(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)");
我在playground上測(cè)試末尾添加兩個(gè)語(yǔ)句股票屬性的負(fù)值,但沒(méi)有其他變化處理能力是必需的郁竟。尤其是玛迄,代碼語(yǔ)句,依靠股票屬性都不知道從存儲(chǔ)的屬性為一個(gè)計(jì)算的變化枪孩。下面是示例應(yīng)用程序運(yùn)行時(shí)產(chǎn)生的控制臺(tái)輸出 ︰
皮劃艇的銷售稅: $10.0
總價(jià)值的股票: $4059.3
庫(kù)存水平的皮劃艇 ︰ 0
最后一條消息顯示計(jì)算屬性的效果 ︰ 我設(shè)置股票屬性設(shè)置為-50憔晒,但該屬性值的時(shí)候,我收到0.
理解的模式的陷阱
要避免的缺陷與此模式選擇模板蔑舞,錯(cuò)誤的這通常意味著要使用結(jié)構(gòu)拒担,當(dāng)一個(gè)類會(huì)更合適。swift的類和結(jié)構(gòu)有很多共同之處攻询,但還有一個(gè)重要的區(qū)別从撼,
在這種模式下 ︰ 結(jié)構(gòu)是值對(duì)象和類的引用對(duì)象。
在可可對(duì)象模板模式的示例
因?yàn)檫@是一個(gè)基本的模式钧栖,類和結(jié)構(gòu)可以發(fā)現(xiàn)整個(gè)可可框架和內(nèi)置swift類型低零。作為結(jié)構(gòu),實(shí)現(xiàn)基本的類型拯杠,如字符串掏婶、 數(shù)組和字典和類用于表示一切從網(wǎng)絡(luò)連接到用戶界面組件。我不會(huì)對(duì)列表中所有的類和結(jié)構(gòu)潭陪,用于 iOS 和可可框架雄妥,但是如果你想要如何一種深深植根這種模式是在 iOS 開(kāi)發(fā),看一看我用于創(chuàng)建 SportsStore 應(yīng)用程序的類依溯。除了產(chǎn)品類我創(chuàng)建了在這一章老厌,我有依靠 NSNumberFormatter 格式化貨幣字符串 UIViewController 來(lái)管理應(yīng)用程序和類,如所提的意見(jiàn) UILabel, UITextField黎炉,和 UIStepper 來(lái)預(yù)設(shè)布局組件向用戶枝秤。
將模式應(yīng)用到 SportsStore 應(yīng)用程序
準(zhǔn)備示例應(yīng)用程序
創(chuàng)建一個(gè)文件,我將使用定義并不直接相關(guān)慷嗜,設(shè)計(jì)模式的實(shí)用功能淀弹。要將新文件添加到項(xiàng)目丹壕,控制-點(diǎn)擊 SportsStore 文件夾項(xiàng)目導(dǎo)航器中,從菜單中選擇新的文件垦页。Xcode 將呈現(xiàn)不同的文件類型雀费,
選擇從 iOS 的 Swift 文件?源類別并單擊下一步按鈕。設(shè)置文件名稱為 Utils.swift 痊焊,并確保 SportsStore 在目標(biāo)列表中盏袄,選中,
創(chuàng)建的 Product.swift 文件
Xcode 將創(chuàng)建新文件并打開(kāi)它進(jìn)行編輯薄啥。 如何使用該文件來(lái)定義 Utils 類辕羽。
Utils.swift File 的內(nèi)容
import Foundation;
class Utils {
class func currencyStringFromNumber(number:Double) -> String {
let formatter = NSNumberFormatter();
formatter.numberStyle = NSNumberFormatterStyle.CurrencyStyle;
return formatter.stringFromNumber(number) ?? "";
} }
我定義了調(diào)用的方法類型 (也稱為靜態(tài)方法)currencyStringFromNumber接受雙值和數(shù)字格式設(shè)置為貨幣值的回報(bào)。例如垄惧,值 1000.1 將格式化為字符串 $1,000.10刁愿。(貨幣符號(hào)是基于應(yīng)用的設(shè)備的區(qū)域設(shè)置。在美國(guó)到逊,美元符號(hào)可能被替換另一個(gè)符號(hào)铣口,如那些為歐元或英鎊。)
字符串的格式不是我描述在此表中觉壶,所以我已經(jīng)定義在此代碼的模式的一部分Utils.swift要保持它的出路的文件脑题。
當(dāng)我將添加到底部的 SportsStore 布局標(biāo)簽所顯示的信息,我將使用新的類型方法铜靶。
創(chuàng)建產(chǎn)品類
正如我在"理解 Swift 訪問(wèn)控制"邊欄叔遂,解釋private關(guān)鍵字限制訪問(wèn)并不在同一個(gè)類文件中定義的代碼。因?yàn)槲蚁胍獜?qiáng)調(diào)由這種模式提供的公鑰/私鑰分離争剿,要?jiǎng)?chuàng)建一個(gè)新文件并使用它來(lái)定義產(chǎn)品類已艰。在上一節(jié)中描述的過(guò)程添加一個(gè)文件稱為 Product.swift SportsStore 項(xiàng)目并使用它來(lái)定義類。
Product.swift File 的內(nèi)容
class Product {
private(set) var name:String;
private(set) var description:String;
private(set) var category:String;
private var stockLevelBackingValue:Int = 0;
private var priceBackingValue:Double = 0;
init(name:String, description:String, category:String, price:Double,
stockLevel:Int) {
self.name = name;
self.description = description;
self.category = category;
self.price = price;
self.stockLevel = stockLevel;
}
var stockLevel:Int {
get { return stockLevelBackingValue;}
set { stockLevelBackingValue = max(0, newValue);}
}
private(set) var price:Double {
get { return priceBackingValue;}
set { priceBackingValue = max(1, newValue);}
}
var stockValue:Double {
get {
return price * Double(stockLevel);
}
} }
提出了重點(diǎn)是public的表示與private實(shí)施蚕苇,取得了在幾種方法的分離所示的類哩掺。第一種方法是通過(guò)對(duì)添加注釋的屬性與private或 private(set)。private關(guān)鍵字可以隱藏不管它適用于從代碼在當(dāng)前文件涩笤,及這效果的制作 priceBackingValue 和 stockLevelBackingValue 屬性完全看不到 SportsStore 應(yīng)用的其余部分因?yàn)楫a(chǎn)品類是唯一永恒的東西 Product.swift 文件疮丛。
注釋具有的屬性private(set)意味著屬性可以從同一模塊中的其他文件中的代碼讀取,但只有在由代碼設(shè)置Product.swift文件辆它。我用 private(set) 的屬性在清單 4-13,大部分具有允許履恩,否則為不得設(shè)置使用傳遞給類初始值設(shè)定項(xiàng)的參數(shù)值的效果锰茉。
提示可有類似的效果,使用常量切心,但是我想要強(qiáng)調(diào)在這一章飒筑,對(duì)象模板模式和private(set)是一個(gè)更有用的例子片吊。
我用另一個(gè)方法是計(jì)算屬性,該屬性定義了只有獲取子句协屡。計(jì)算屬性的實(shí)現(xiàn)是private的即使該屬性本身是整個(gè)當(dāng)前模塊可用俏脊。
了解快速訪問(wèn)控制
swift以不尋常的方法來(lái)訪問(wèn)控制,能夠找出粗心肤晓。有三個(gè)級(jí)別的訪問(wèn)控制爷贫,這使用應(yīng)用public, private,和internal關(guān)鍵字补憾。private關(guān)鍵字是限制性最強(qiáng)的; 它限制對(duì)類漫萄、 結(jié)構(gòu)方法和屬性的定義在同一文件中的代碼訪問(wèn)。限制訪問(wèn)每個(gè)文件的基礎(chǔ)上從大多數(shù)語(yǔ)言的不同方法盈匾,并且意味著private在 Xcode 沒(méi)有影響
兒童游樂(lè)場(chǎng)腾务。
internal關(guān)鍵字表示當(dāng)前模塊內(nèi)允許訪問(wèn)。這是默認(rèn)級(jí)別的訪問(wèn)控制削饵,如果沒(méi)有關(guān)鍵字應(yīng)用使用岩瘦。對(duì)于大多數(shù)的 iOS 開(kāi)發(fā), internal保護(hù)會(huì)允許類窿撬、 結(jié)構(gòu)启昧、 方法、 函數(shù)或?qū)傩杂糜谡麄€(gè)項(xiàng)目的效果尤仍。
public關(guān)鍵字適用限制性最小的控制級(jí)別箫津,并允許從任何地方,包括當(dāng)前模塊外部訪問(wèn)宰啦。這是給開(kāi)發(fā)商誰(shuí)正在創(chuàng)建框架和誰(shuí)將需要使用的大多數(shù)使用的public關(guān)鍵字定義的框架給其他開(kāi)發(fā)人員提供了 API苏遥。
如果你已經(jīng)搬到swift從一種語(yǔ)言如 C# 或 Java,然后你可以最密切重新創(chuàng)建你習(xí)慣于通過(guò)定義每個(gè)類或結(jié)構(gòu)在其自己的訪問(wèn)控制.swift文件和使用private和internal的訪問(wèn)級(jí)別赡模。
ViewController.swift File 中的產(chǎn)品類
import UIKit
class ProductTableCell : UITableViewCell {
@IBOutlet weak var nameLabel: UILabel!
@IBOutlet weak var descriptionLabel: UILabel!
@IBOutlet weak var stockStepper: UIStepper!
@IBOutlet weak var stockField: UITextField!
var product:Product?;
}
class ViewController: UIViewController, UITableViewDataSource {
@IBOutlet weak var totalStockLabel: UILabel!
@IBOutlet weak var tableView: UITableView!
var products = [
Product(name:"Kayak", description:"A boat for one person",
category:"Watersports", price:275.0, stockLevel:10),
Product(name:"Lifejacket", description:"Protective and fashionable",
category:"Watersports", price:48.95, stockLevel:14),
Product(name:"Soccer Ball", description:"FIFA-approved size and weight",
category:"Soccer", price:19.5, stockLevel:32),
Product(name:"Corner Flags",
description:"Give your playing field a professional touch",
category:"Soccer", price:34.95, stockLevel:1),
Product(name:"Stadium", description:"Flat-packed 35,000-seat stadium",
category:"Soccer", price:79500.0, stockLevel:4),
Product(name:"Thinking Cap",
description:"Improve your brain efficiency by 75%",
category:"Chess", price:16.0, stockLevel:8),
Product(name:"Unsteady Chair",
description:"Secretly give your opponent a disadvantage",
category: "Chess", price: 29.95, stockLevel:3),
Product(name:"Human Chess Board",
description:"A fun game for the family", category:"Chess",
price:75.0, stockLevel:2),
Product(name:"Bling-Bling King",
description:"Gold-plated, diamond-studded King",
category:"Chess", price:1200.0, stockLevel:4)];
override func viewDidLoad() {
super.viewDidLoad()
displayStockTotal();
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func tableView(tableView: UITableView,
numberOfRowsInSection section: Int) -> Int {
return products.count;
}
func tableView(tableView: UITableView,
cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { let product = products[indexPath.row]
let cell = tableView.dequeueReusableCellWithIdentifier("ProductCell") as ProductTableCell
cell.product = products[indexPath.row]
cell.nameLabel.text = product.name
cell.descriptionLabel.text = product.description
cell.stockStepper.value = Double(product.stockLevel)
cell.stockField.text = String(product.stockLevel)
return cell;
}
@IBAction func stockLevelDidChange(sender: AnyObject) {
if var currentCell = sender as? UIView {
while (true) {
currentCell = currentCell.superview!;
if let cell = currentCell as? ProductTableCell {
if let product = cell.product? {
if let stepper = sender as? UIStepper {
product.stockLevel = Int(stepper.value);
} else if let textfield = sender as? UITextField {
if let newValue = textfield.text.toInt()? {
product.stockLevel = newValue;
} }
cell.stockStepper.value = Double(product.stockLevel);
cell.stockField.text = String(product.stockLevel);
}
break; }
}
displayStockTotal();
}
}
func displayStockTotal() {
let stockTotal = products.reduce(0,
{(total, product) -> Int in return total + product.stockLevel}); totalStockLabel.text = "\(stockTotal) Products in Stock";
} }
過(guò)渡到使用產(chǎn)品類是簡(jiǎn)單的田炭。在編寫(xiě)代碼清單 4-14,我開(kāi)始用在類產(chǎn)品數(shù)據(jù)數(shù)組漓柑,然后修正所有的編譯器錯(cuò)誤教硫,直到被替換了對(duì)元組的所有引用。這是一個(gè)乏味而且容易出錯(cuò)的過(guò)程辆布,這就是為什么它是一個(gè)好的主意瞬矩,開(kāi)始與類和結(jié)構(gòu)的一個(gè)項(xiàng)目,如果你能 (某物锋玲,可悲的是景用,并不總是可行時(shí)接管現(xiàn)有的代碼)。
確保視圖和模型分離
有幾個(gè)點(diǎn)要注意有關(guān)清單 4-14 中的代碼惭蹂。第一個(gè)是伞插,
ViewController.swift文件定義了一個(gè)名為類ProductTableCell用于包含表示應(yīng)用程序布局中的產(chǎn)品的 UI 組件的引用割粮,并找到一種產(chǎn)品,當(dāng)用戶更改庫(kù)存水平媚污。在清單 4-14舀瓢,我取代提到一個(gè)元組中的索引位置變量產(chǎn)品數(shù)組引用產(chǎn)品對(duì)象相反,像這樣 ︰
class ProductTableCell : UITableViewCell {
@IBOutlet weak var nameLabel: UILabel!
@IBOutlet weak var descriptionLabel: UILabel!
@IBOutlet weak var stockStepper: UIStepper!
@IBOutlet weak var stockField: UITextField!
var product:Product?;
}
你可能想知道為什么我沒(méi)有結(jié)合ProductTableCell與產(chǎn)品類耗美,并有一個(gè)單一的實(shí)體表示產(chǎn)品和用來(lái)顯示它的 UI 組件京髓。我解釋第 5 部分詳細(xì)的原因,當(dāng)我描述模型幽歼,視圖朵锣,控制器 (MVC) 模式,但簡(jiǎn)短的回答是很好的實(shí)踐甸私,它呈現(xiàn)給用戶 (在 MVC 的說(shuō)法诚些,將模型與視圖分離) 的方式從應(yīng)用程序中對(duì)數(shù)據(jù)進(jìn)行分離。執(zhí)行這種分離允許更容易地以不同的方式顯示相同數(shù)據(jù)皇型。我可能需要第二個(gè)視圖添加到應(yīng)用程序诬烹,在網(wǎng)格中,提出了產(chǎn)品和不經(jīng)分離模型和視圖之間弃鸦,組合的類需要能夠?qū)γ總€(gè)參與這兩種觀點(diǎn)的很快變得笨拙 UI 組件的引用并使應(yīng)用更改一個(gè)棘手且容易出錯(cuò)的過(guò)程绞吁。
擴(kuò)大摘要顯示
我一直在批評(píng)的元組在整個(gè)這一章,但他們可以當(dāng)他們使用在自足的方式唬格,而不是代表整個(gè)應(yīng)用程序范圍的數(shù)據(jù)的一種有用的語(yǔ)言功能家破。
你可以看到一個(gè)例子的怎樣使用元組。
我已經(jīng)改變了的執(zhí)行 displayStockTotal 方法的 ViewController 類购岗,以便單個(gè)調(diào)用到全球減少函數(shù)用于計(jì)算股票中的項(xiàng)目數(shù)和該股票的總價(jià)值 (其中使用設(shè)置格式 currencyStringFromNumber 方法定義在清單 4-12)汰聋。
在 ViewController.swift File 中使用元組
...
func displayStockTotal() {讓 finalTotals:(Int, Double) = products.reduce (0 (0.0),
{(totals, product)-> (Int喊积,Double) in
return (
totals.0 + product.stockLevel烹困,totals.1 product.stockValue +
);
});
totalStockLabel.text ="\(finalTotals.0) 在庫(kù)存中的產(chǎn)品"。
+"總價(jià)值 ︰ \(Utils.currencyStringFromNumber(finalTotals.1))";
} ...
元組讓我產(chǎn)生兩個(gè)總計(jì)值 (一個(gè)用于股票中的項(xiàng)目數(shù))乾吻,一個(gè)為這支股票的價(jià)值為每次迭代中減少函數(shù)髓梅。能取得這以不同的方式 — — 如通過(guò)定義一個(gè)結(jié)構(gòu),具有兩個(gè)屬性绎签,或通過(guò)使用為循環(huán)來(lái)枚舉數(shù)組并更新兩個(gè)局部變量 — — 但使用元組與swift關(guān)閉很好地工作和生產(chǎn)是簡(jiǎn)單和易讀的代碼枯饿。這種使用,創(chuàng)建一個(gè)類或結(jié)構(gòu)將會(huì)矯枉過(guò)正诡必,因?yàn)橥饷娴姆椒ㄑ寄悖瑢?dǎo)出數(shù)據(jù),不是元組的下懷,不會(huì)引起出現(xiàn)傳遞更廣泛地在應(yīng)用程序中的元組時(shí)的緊耦合和維護(hù)問(wèn)題袱巨。
你可以看到額外的總數(shù)的效果我通過(guò)啟動(dòng)應(yīng)用程序來(lái)計(jì)算。該標(biāo)簽底部的布局將顯示的數(shù)量和項(xiàng)目?jī)r(jià)值的股票
摘要
在本章中碳抄,我描述了一種模式愉老,在swift發(fā)展的核心是 ︰ 定義一個(gè)用于創(chuàng)建對(duì)象的模板。這種模式的好處是剖效,它提供了基本的工具嫉入,可以用來(lái)打破緊耦合的組件分開(kāi),允許一個(gè)public API 來(lái)呈現(xiàn)給消費(fèi)者的一個(gè)對(duì)象和一個(gè)隱藏的私有實(shí)現(xiàn)璧尸。
在下一章咒林,我轉(zhuǎn)向創(chuàng)建對(duì)象的不同的方式 ︰ 使用原型.