面向協議的 MVVM 架構介紹
在 Swift 中用值型別來替代引用型別,比以前在 Objective-C 中要容易許多,這可以讓您的程式碼更簡潔,並且更不容易出錯。然而,當需要在多個型別當中共享程式碼的時候,許多人往往會迴避使用值型別,而轉為使用繼承來實現。
通過 Natasha 在 do{iOS} 2015 上對 MVVM 的介紹,您可以學習到如何使用協議來實現這個功能,而不再採用繼承的方式!Natasha The Robot 將會引導您跟隨她學習和使用面向協議程式設計的過程,使用 Swift 2.0 的特性來建立漂亮、穩定的程式碼。
About the Speaker: Natasha Murashev
Natasha 喜歡學習 Swift 和 iOS 開發,她暗喻自己是一名“機器人”。此前,她曾經在位於舊金山 Capital One 公司工作,任職 iOS 資深工程師,但是如今她正四處旅行,撰寫她學習新技術的心得體會。在她空餘的時候,她會將時間花在她的個人專案、在聚會沙龍和大會上進行演講、向開源專案貢獻程式碼,以及實現她願望清單上列出的各項事宜。
為什麼要簡化 Swift 中的程式碼呢? (0:00)
嗨,我是 Natasha。就是 Twitter 上的 @NatashaTheRobot。關於我的個人介紹的話我還想多說一些:我有一個 每日 Swift 週報,還有一個寫了許多關於 Swift 文章的部落格,自 Swift 第一天推出以來,我就已經在大量地研究 Swift 了。
作為一名 Objective-C 開發者,我基本上是在 Swift 剛剛推出的時候開始學習 Objective-C 程式碼的——這導致我使用了很多的引用型別。我習慣性將所有東西宣告為類,因為我已經習慣了面向物件程式設計。這也正是 Objective-C 的思想。
我覺得我自己的程式設計習慣還是很不錯的了,因為我有些時候還是會使用列舉的。它們比普通的列舉要複雜得多,這讓我覺得很不錯。然而,當我開始參加各種活動然後聽取演講之後,尤其是 Andy Matuschak 關於控制 Swift 程式碼複雜度的演講之後,我就豁然開朗了。他提到要使用值型別。直到這個時候,我才知道 Swift 當中有結構體,但是作為一名從 Objective-C 轉向 Swift 的開發者,對我來說以類來起步是很自然的一件事情。
這次演講給我留下了很深的印象,我覺得應該要儘可能將所有東西都應用上值型別。事實上,他發現絕大多數 Swift 標準庫中的東西都是使用的值型別,並且語言的創造者本身也在使用值型別。我回到我的工作專案當中,然後建立了一個新檔案,準備從此開始實驗值型別的強大之處。
最初,我有這樣一個感覺:“我是一名 Swift 開發者了!我在使用結構體了!”,但是後來有些需求不得不讓我轉而使用繼承,這感覺非常糟糕,但是我不知道該如何解決這個問題,直到……
面向協議程式設計 (2:28)
在今年的 WWDC 上,有一個難以置信的演講,講述了在 Swift 中進行面向協議程式設計。在這裡,他們解釋瞭如何用協議來替代繼承。如果您沒有看過這個演講並且打算轉向 Swift 進行開發的話,那麼我覺得這是自去年的 WWDC 以來,最為重要的一個演講了。
在這個演講中, Apple Swift 標準庫的技術總監 Dave Abrahams 提到了:
“Swift 是一門面向協議的程式語言。”
在視訊當中,他演講的標題是“醍醐灌頂的講解 (Professor of Blowing Your Mind)”,他成功做到了這一點,我以及每一名觀眾都能感受到這一點。
這對我們來說並不是一個全新的概念:我們已經見識過 Apple 使用了大量的協議,比如說 TableView 當中,我們都覺得這是一個很棒的設計模式,因為沒有人希望每時每刻都要繼承 UITableViewController。相反,您可以使用協議來告訴 Apple 您需要多少個表檢視單元格。我們已經知曉了這種設計模式的魅力所在,而現在我們需要把它帶到一個全新的高度。
對於我來說,我對此非常興奮。我已經迫不及待回到電腦面前,更詳細地研究這個設計模式,成為一名“面向協議的程式設計師”。我隨後帶著興奮回去處理我的那些工作。在我的工作當中,我已經有了一個正在使用的程式碼庫,它當中帶有了已經確定並建立的模式。這很難向其中加入新的東西,也很難理解應該如何使用它。我想要使用面向協議程式設計,但是我覺得我已經受限於我的既有專案了,我不知道該如何才能更進一步。
MVVM - 將事情留到第二天再考慮 (5:00)
我的腦海裡一直在思考著關於協議的相關內容,我在想“我該怎麼將協議整合到我的程式碼當中呢?”這件事情一直停留在我的腦海裡,揮之不去,但是我不知道該如何做到這一點。然後,我就去睡了一覺。我非常強烈推崇這種做法,儘管對於程式設計師來說,他們的聲譽往往在於“不達目的不罷休”。對於我來說,睡一覺可以幫助我解決很多問題,很可能是因為我們的大腦在睡眠的時候可以更好地處理資訊。
睡了一覺之後,我醒來發現所有的東西都迎刃而解了。我想我至少能夠在我的工作程式碼中應用一個用例,這讓我十分興奮。這個用例就是使用 MVVM。
對那些不熟悉 MVVM 的人來說,您可以去閱讀 Ash Furrow 的這篇博文:Swift 中的 MVVM。同樣在 objc.io 上還有一篇叫做 “MVVM 介紹” 的文章。我會使用一個簡單的例子來介紹,所以希望大家能夠看到 MVVM 是如何工作的。
我曾經在一家銀行工作。比如說您有一個模型,裡面包含了關於賬戶餘額的一些原始資料。在模型層當中,您想要保留這個值,作為原始的 NSDecimalNumber。
let amount = 6729383.99
當您向用戶展示相同數字的時候,您可能想要轉換其顯示樣式,例如說:“您的賬戶餘額為……”,然後在您的檢視當中新增 “$” 標識,並且進行格式化:
Your balance is $6,729,383.99
許多人喜歡將這種程式碼放到檢視控制器當中。這往往會導致檢視控制器變得臃腫不堪,從而難以測試。此外,您還可能將這種程式碼放到模型當中,從而讓模型也變得非常難看,因為有許多進行格式化的程式碼擠在其中。
相反,您可以讓模型變得清晰,然後僅僅只用於對映您的原始資料。這是您檢視模型的初始狀態:
struct AccountViewModel {
let displayBalance: String
init(mode: BankAccount) {
let formattedBalance = model.balance.currencyValue
displayBalance = "Your balance is \(formattedBalance)"
}
}
您的檢視模型實際上會讀取您的資料模型,然後將其中的資訊進行格式化,從而準備展示到檢視當中。這就是檢視模型的魅力所在。這很容易進行測試。您可以將帶有賬戶資訊的模型放進去,然後測試顯示就可以了,而在此之前,如果您想要測試您的檢視控制器或者檢視,這是非常非常難的,因為輸出特別紛繁複雜。
Zoetrope 模型 (8:29)
注意到我的檢視模型是值型別的。那麼這個在 Swift 中是如何起作用的呢?
關鍵在於,您的檢視控制器需要維持檢視模型的最新版本。值型別是一種資料型別。它不應該成為真實的資料,它只是資料在某個時間點的一份拷貝而已。您的檢視控制器需要跟蹤這些資訊,決定哪個拷貝資料應該展示給使用者(也就是最新的拷貝)。
順便想想,在 Andy Matuschak 的演講中,那個關於 zoetrope 的例子。(在日本的 Ghibli 博物館中有這樣一個很神奇的西洋鏡)。
這裡的關鍵在於,zoetrope 的每一個幀都是靜態值。您可以通過改變人物手部擡起的距離,或者人物頭部傾斜的角度,來對字元進行編碼。每一幀都是靜態的,但是當您把它們放到一起,然後一直看向一箇中心的話,那麼始終都會有新的資料出現,這樣您就可以得到一個美麗、生動的動畫。
您可以用相同的方式來實現值型別。您的檢視控制器將會跟蹤 zoetrope 的最後一個幀影象——也就是最新的一塊活躍資料,然後將其展示給使用者。只要您的模型發生了更新,也就是有了新的資料,這樣您就可以通過計算得到一個新的檢視模型。現在,您的檢視就會根據最新的資訊進行更新了。
var viewModel = ViewModel(model: Account)
沒有協議之前的醜陋 (9:57)
現在,我們已經得到了令人興奮的部分了。我現在將會執行一個非常簡單的例子。在這個表檢視當中,比如說絕大多數應用都會有的設定螢幕,試想我只有一個設定:用一個滑塊 (slider) 來將整個應用主色調變為黃色。
這個操作應該是非常簡單的,但是它也會變得很複雜。這裡有一個問題:在我們的表檢視單元格當中,其中的每一個單獨元件都需要以某種方式來進行格式化。如果其中有標籤 (label) 的話,那麼您必須要定義它的字型,字型顏色,字型大小,等等。如果是開關 (switch) 的話,那麼當開關開啟的時候會發生些什麼?初始狀態是關閉還是開啟?對於這種擁有這兩個元素的簡單的表檢視單元格來說,我已經有 6 種不同的方式來對它進行配置:
class SwitchWithTextTableViewCell: UITableViewCell {
func configure(
title: String,
titleFont: UIFont,
titleColor: UIColor,
switchOn: Bool,
switchColor: UIColor = .purpleColor(),
onSwitchToggleHandler: onSwitchTogglerHandlerType? = nil)
{
// 在這裡配置檢視
}
}
您可以想象得到,我們絕大多數人進行配置的表檢視單元格比著遠複雜得多。在我的程式碼當中,這種 configure 方法將非常非常累贅。新增一個副標題將會導致多出額外的三個屬性需要設定。在 Swift 中您可以用預設值來獲得一些輔助,但是使用這種臃腫的 configure 方法不是非常簡潔。
在您實際上呼叫此方法的檢視控制器當中,我們持有了所有存放在其中的資訊棧。它看起來並不是很好看;這讓人感覺很不好,但是我一直沒想到有更好的辦法,直到協議的出現。
檢視模型及協議 (12:05)
對於單元格來說,我們不應該使用這些臃腫的配置方法,而是應該將每個部分單獨拿出來,然後將其放大一個 SwiftchWithTextCellProtocol 的協議當中。這讓我感覺到非常開心。這樣子,我就可以讓我的檢視模型實現這個協議,然後在這裡設定所有的屬性。現在,我就不用再去使用臃腫的配置方法了,但是我仍然需要有一種方式來確保每個單獨的屬性實際上都被設定了。
protocol SwitchWithTextCellProtocol {
var title: String { get }
var titleFont: UIFont { get }
var titleColor: UIColor { get }
var switchOn: Bool { get }
var switchColor: UIColor { get }
func onSwitchToggleOn(on: Bool)
}
通過 Swift 2.0 當中的協議擴充套件,我就可以通過預設值做一些處理了。如果對於大多數單元格來說,可以確定某一種顏色的話,那麼您就可以對其建立擴充套件,然後設定該顏色即可。所有的實現此協議的檢視模型都沒必要再去設定這個顏色了。這個做法非常棒:
extension SwitchWithTextCellProtocol {
var switchColor: UIColor {
return .purpleColor()
}
}
現在,我的 configure 方法只需要獲取某個實現此協議的值就可以了:
class SwitchWithTextTableViewCell: UITableViewCell {
func configure(withDelegate delegate: SwitchWithTextCellProtocol)
{
// 在這裡配置方法
}
}
這個方法只有一個引數,這對之前的那個六個引數(甚至更多)的方法來說是一個重大的改進。這是我現在的檢視模型的一個示例:
struct MinionModeViewModel: SwitchWithTextCellProtocol {
var title = "Minion Mode!!!"
var switchOn = true
var switchColor: UIColor {
return .yellowColor()
}
func onSwitchToggleOn(on: Bool) {
if on {
print("The Minions are here to stay!")
} else {
print("The Minions went out to play!")
}
}
}
它實現了這個協議,然後配置了所有相關的資訊。正如您在前面的示例中看到的那樣,您可以用您的模型物件來初始化檢視模型了。現在,如果您需要諸如外匯收益之類的資訊的話,您實際上可以在您檢視模型的各個地方使用這個資訊,以便能夠指明如何對其進行配置,並將檢視展示出來。
因此,這個操作將會非常簡單。現在,我的 cellForRowAtIndexPath() 也變得非常的簡明瞭:
// YourViewController.swift
let cell = tableView.dequeueReusableCellWithIdentifier("SwitchWithTextTableViewCell", forIndexPath: indexPath) as! SwitchWithTextTableViewCell
// This is where the magic happens!
cell.configure(withDelegate: MinionModeViewModel())
return cell
我將單元格 dequeue 出來,然後呼叫了我檢視模型的 configure 方法。在這個例子當中,我沒有對它的 frame 進行任何的配置,它同樣也沒有包含模型層,但是您同樣可以將這個模型放到檢視控制器層級,以便對其進行跟蹤。您同樣可以在檢視模型當中傳遞這些資訊,這樣您的單元格就可以生成了。當我們重構之後,我們只需要三行程式碼就可以完成配置了。
進一步的抽象 (14:10)
這個時候,我為自己的做法感到非常開心。因為我把這個臃腫的帶有六個引數的 configure 方法,用協議的方式將其進行了重構。我發現使用協議能夠讓我的程式碼更優美、更簡潔,邏輯更清晰。
通常情況下,我的下一步動作就是通過部落格把它發表出來。我喜歡為了總結學習經驗而寫部落格,因此無論我是學到了什麼還是發現了什麼,我都會在部落格中把它寫出來。我的部落格上已經講述了這一點,有人發帖評論說:“有沒有考慮建立兩個協議呢?一個作為實際編碼資訊的資料來源,就比如說單元格的標題之類的東西,也就是實際的資料。”和顏色、字型之類的資訊不同,它們應該是相互獨立的,因為字型之類的資訊更多是關於格式化方面的,而其中並沒有包含實際的資料,並且這種模式我們已經可以看到 Apple 用過了,比如說在 UITableViewCells 或者集合檢視之類的地方。
我認為這是一個非常絕妙的想法。我將我的邏輯進行了分離,然後再建立了單元格資料儲存和單元格委託:
protocol SwitchWithTextCellDataSource {
var title: String { get }
var switchOn: Bool { get }
}
protocol SwitchWithTextCellDelegate {
func onSwitchToggleOn(on: Bool)
var switchColor: UIColor { get }
var textColor: UIColor { get }
var font: UIFont { get }
}
接下來,我讓我的 configure 方法同時接收這兩個協議。因為委託可以全部在協議擴充套件中使用預設值進行配置,比如說字型、顏色之類的資訊,這樣在理論上我可以不用向裡面傳遞任何東西進去;我可以只用建立一個模型就可以了:
// SwitchWithTextTableViewCell
func configure(withDataSource dataSource: SwitchWithTextCellDataSource, delegate: SwitchWithTextCellDelegate?)
{
// 在這裡配置檢視
}
現在我可以使用擴充套件來改進我的檢視模型了。我會使用一個實現資料來源的程式碼塊,然後給定要傳遞給檢視當中的原始資訊:
struct MinionModeViewModel: SwitchWithTextCellDataSource {
var title = "Minion Mode!!!"
var switchOn = true
}
接下來,我會在一個單獨的檢視模型的部分當中使用處理字型、顏色之類的委託,然後在其中進行相關的配置。
extension MinionModeViewModel: SwitchWithTextCellDelegate {
var switchColor: UIColor {
return .yellowColor()
}
func onSwitchToggleOn(on: Bool) {
if on {
print("The Minions are here to stay!")
} else {
print("The Minions went out to play!")
}
}
}
最終,我的表檢視單元格變得非常簡單:
// SettingsViewController
let viewModel = MinionModeViewModel()
cell.configure(withDataSource: viewModel, delegate: viewModel)
return cell
我僅僅只用建立了我的檢視模型,然後將其傳遞到配置方法當中,然後返回單元格,就完畢了。
Swift 2.0 中的 Mixin 和 Trait (16:32)
我對這一點還是比較滿意的。我建立了協議,簡化了我的程式碼,然後發表了相關的部落格,學習到了相關的知識。接著,我又讀到了一個非常讚的文章,我覺得大家都應該去讀一讀:@mhollemans 寫的 Swift 2.0 中的 Mixin 和 Trait。Matthijs 講述的是遊戲開發,雖然我對此並不是很熟悉,但是我們仍然可以去理解他提到的基本概念。
在遊戲開發當中,通常會有著一個很龐大的層級關係,以及一系列的繼承。比如說“怪物”型別當中,可能會有各種各樣的“怪物”。繼承在這裡變得十分有意義。但是,隨著層級的擴充套件,事情變得開始凌亂起來。
對於這種型別的層次結構來說,剛開始的時候還好。不過隨著後面的發展,當您遇到要設計一個也能夠射擊的怪物的時候,事情就變得麻煩起來了,因為城堡同樣也可以射擊,因為在城堡的頂端擁有大炮,因此您現在就必須要將這個“射擊輔助類”提取出來。當您正在建立這些子類的時候,您會覺得這種做法是非常非常奇異的,但是這很快會變得越來越混亂,最終您將會寫出一團亂麻般的程式碼。
Matthijs 重構了這個程式碼,這樣我們不再使用這些繼承物件的邏輯,比如說控制能夠射擊或者控制能夠治療的子類,而是將其提取成為協議,通過協議擴充套件來實現這個功能。
這使得程式碼看起來更加簡潔,更容易理解。例如:
class ZapMonster: GameObject, GunTrait, RenderTrait, HealthTrait, MovementTrait {
...
}
只需要看一看這個物件的型別,我就可以立刻理解這個物件擁有哪些功能,而不是去一個一個檢視它的實現。我個人更加喜歡這樣的設計模式。
在我們的應用中應用 Mixin (19:47)
雖然剛剛的例子是關於遊戲開發的,但是我希望我也能夠在我自己的程式碼中對錶檢視單元格應用上這個功能。這樣就不用讓我實際的單元格實現這個協議了,我只需要將其與更寬泛的 TextPresentable 聯絡在一起就可以了。這樣,任何擁有標籤的檢視,而不僅僅只是單元格,都可以實現這個協議來完成相關的功能。這樣我就可以說這個標籤當中有什麼樣的文字,什麼樣的顏色,以及什麼樣的字型:
protocol TextPresentable {
var text: String { get }
var textColor: UIColor { get }
var font: UIFont { get }
}
protocol SwitchPresentable {
var switchOn: Bool { get }
var switchColor: UIColor { get }
func onSwitchToggleOn(on: Bool)
}
Switch 擁有自己獨有的協議,這樣就可以知道它應該如何配置了。您可以想象這個從遊戲開發示例當中得來的靈感:現在您需要一個影象了,你只需要實現 ImagePresentable 協議就可以了;現在您需要一個文字框了,只需要實現 TextFieldPresentable 協議就可以了:
protocol ImagePresentable {
var imageName: String { get }
}
protocol TextFieldPresentable {
var placeholder: String { get }
var text: String { get }
func onTextFieldDidEndEditing(textField: UITextField)
}
通過協議擴充套件,您可以配置所有的字型和顏色,因此每一個單獨實現這個 TextPresentable 協議的檢視都會擁有這個標籤的預設配置,因為通常情況下,您應用中的標籤基本上都是非常相似的:
extension TextPresentable {
var textColor: UIColor {
return .blackColor()
}
var font: UIFont {
return .systemFontOfSize(17)
}
}
您甚至可以更進一步,建立不同型別的標籤,比如說標題標籤。或許它擁有確定的字型或者顏色,這就意味著您可以一遍又一遍地在您的應用程式中重用這個標籤。這樣當您的設計師要求將所有的標題顏色變成藍色的時候,這種做法將會非常快速。您可以前往協議擴充套件當中,將其改變為藍色,然後通過這一行程式碼的變化,每一個擁有這個 HeaderTextPresentable 協議的檢視中的標籤都會立刻改變。
我十分喜歡這個設計模式。這是我現在單元格的模樣:
class SwitchWithTextTableViewCell<T where T: TextPresentable, T: SwitchPresentable>: UITableViewCell {
private var delegate: T?
func configure(withDelegate delegate: T) {
// 在這裡配置檢視
}
}
在這種情況下,它沒有實現這些協議,但是它會期待某種實現這些協議的東西傳遞進去,因此我們使用了泛型。這個單元格期待一個實現了 TextPresentableProtocol 以及 SwitchPresentableProtocol 的委託。這個配置方法並不關心傳遞進去的物件。就我們而言,傳遞進去的將是一個檢視模型,但是它所想要的只要是實現了這些協議的東西就可以了,現在,您就可以基於這些資訊在單元格當中配置所有東西了。
extension MinionModeViewModel: TextPresentable {
var text: String { return "Minion Mode" }
var textColor: UIColor { return .blackColor() }
var font: UIFont { return .systemFontOfSize(17.0) }
}
我們的檢視模型將擁有一個 TextPresentable 程式碼塊,在其中您可以配置文字、顏色、字型,並且由於所有這些在協議擴充套件當中都已經有預設值了,您甚至都不用讓檢視模型去實現這些具體的內容。
對於 SwitchPresentable 也是一樣的。這個開關應該開啟還是關閉?當開關開啟的時候應該發生些什麼?這裡,您可以看到這個檢視的一小部分:
extension MinionModeViewModel: Switch Presentable {
var switchOn: Bool { return false }
var switchColor: UIColor { return .yellowColor() }
func onSwitchToggleOn(on: Bool) {
if on {
print("The Minions are here to stay!")
} else {
print("The Minions went out to play!")
}
}
}
最後,檢視控制器當中的程式碼就變得十分簡單:您只需要 dequeue 相應的單元格。然後通過檢視模型對其進行配置,然後返回單元格即可。其中一個關鍵的地方是,因為我們使用的是泛型,因此我們必須要指明 T 是什麼東西,在我們的例子當中,T 是檢視模型。
Swift: 一個正在發展的語言 (24:02)
在這一點上我是非常興奮的。我已經經歷了三種不同的迭代版本了。然而,Swift 仍然是一門新語言,它只出現了不到兩年的時間。在這個過程中它變化了很多,我們作為一個社群必須要決定 Swift 的最佳用例是什麼。
我一直在想一件事情,當我在我的程式碼中發現或者是提出某個新的設計模式的時候,該如何才能夠輕鬆地進行遷移。這使得我切實相信:
世上唯一不變的事就是變化本身。
這句話特別適用於程式設計界。我通常會花費一個季度的時間來重寫我的每個應用,因為最終您可能會需要改變很多東西;您需要新增單元測試,或者需要為了 iOS 7 重新設計。Apple 有時候會推出新的東西,因此您可能需要刪除或者新增新的功能;任何東西都在不斷的變化。
因此,我總是在認定我的程式碼即將被改變的這種假設下進行工作的。對於長期的產品開發來說,我必須要考慮到我正在用的這個設計模式是否允許我簡單地進行修改,而不是對一個類進行一個細小的變化就得祈禱這個操作不會發生崩潰。對於我來說,這個設計模式是非常讚的,因為它允許快速地進行修改。
假如說我的產品經理過來跟我說:“哎,對於這個單元格,我希望讓它能夠新增一個影象”。剛開始的時候,我們只有標籤和開關,而現在只是多了一個影象而已。因此,我會讓這個單元格期待一個還實現了 ImagePresentableProtocol 的東西傳遞進去,這就是我在單元格層面所做的全部操作。
我同樣也必須要更新我的配置方法,以便能夠讓其能夠真正使用上這個影象,但是這隻需要使用兩行程式碼就可以了。最後,我只是對我的檢視模型進行擴充套件就可以了。
extension MinionModeViewModel: ImagePresentable {
var imageName: String { return "minionParty.png" }
}
開心的 Natasha the Robot:總結 (26:26)
這個時候我是非常開心的。這些就是我所要討論的所有東西了,在 MVVM 架構中使用協議。
- 使用協議來配置您的檢視
- 使用協議擴充套件來實現預設值——這就是您設定用在您應用當中的所有字型、顏色以及配置的地方。這裡最大的障礙就是處理子類了;這就是人們為什麼總是使用繼承——因為需要在多個類當中使用相同的功能。
- 使用檢視模型來為協議提供資料。您使用檢視模型的目的在於它們易於測試,並且變化帶來的耦合度很小。您的檢視控制器可以決定在最新版本的程式碼當中使用哪個版本的檢視模型。
重點是我希望在明年的 WWDC 中,他們能夠提出一個適用於 Swift 的全新架構,或者有人能夠在這個演講之後在 Tweet 上給我一個更好的主意。對於那些使用 Swift 開發的人來說,我建議大家保持一個開放的心態。想想您該如何讓程式碼變得更好。
聽從您的同事、社群的建議。總有可以改善程式碼的方式的,並且我確信我可以藉此來進行程式碼的改善。在這一點上,我是非常高興的,不過我下週可能會讀到某些文章,這可能會導致我改變主意,或者學到一些新的東西。
關於 Swift 的一個很酷的事情是,我們都有不同的程式設計經驗。您的同事可能是函數語言程式設計語言出身,也有可能是 Ruby 或者 .NET 出身——他們可能有與你不同的想法。由於 Swift 是一個不斷髮展的語言,因此您需要虛心向別人學習。這能夠改善您的設計模式,並且能夠幫助您找到最好的設計模式。我認為您總是可以對其進行優化和改進,分享您的發現,然後周而復始。