MVVM VS MVC:透過 MVVM 設計模式重構 MVC 應用程式 減低應用程式的複雜性


在 iOS 開發人員維護軟體工程時,設計模式是一項非常重要的工具。我將在下文介紹一些設計模式、和最佳的實踐方式,希望可幫助開發人員創建可靠並可維護的應用程式,換句話說,設計模式可以幫助你管理軟體的複雜性

在本教程中,我將會介紹 “Model-View-ViewModel” (MVVM) 設計模式。為了從發展歷程和實用角度切入,我將以非常有名的 “Model-View-Controller” (MVC) 設計模式作比較,MVC 一直以來受到許多 iOS 開發人員的青睞,儘管如此,MVVM 卻同樣在開發人員中持續獲得關注。

為了幫助讀者理解這些設計模式,我將引導你完成下列顯示應用程式的設計和編碼:

App Demo

我是利用 MVVM 建構這個應用程式的,但本文會先使用 MVC 來建置應用程式的原型,然後將其重構為 MVVM 設計模式。

甚麼是設計模式?

Apple 官方將設計模式 (Design Pattern) 定義為:

⋯⋯「解決 context 內的問題。」讓我們逆向解題,先來解析這句話。Context 是指一個反復出現的情況;而問題就是你在 context 中嘗試實現的目標、以及 context 帶來的任何約束;解決方案就是你想達到的事:為實現目標和解決約束的一般設計。

一個設計模式提煉出具體設計結構的關鍵,而該設計已被證明是有效的。模式具有名稱,並可以識別適用的類別和對象、及其職責和協作,它還說明了後果(成本和收益)和可以應用模式的情況。一個設計模式是特定設計的一種模板或指南,從某種意義上說,具體設計是一個模式的「實例化」。設計模式不是絕對的,在應用層面存在一些靈活性,例如在編程語言和現有體系結構等將影響如何應用模式。 ⋯⋯

Apple、MVC 與 MVVM

讓我先定義一些初步的術語,然後再深入探討 MVC 和 MVVM 的機制。請注意,若對 Apple 的 “Developer” 入口網站搜索 “MVVM” 關鍵字,將得到 “No results were found. Please try a different keyword. (無符合資料,請使用其它關鍵字)” 的訊息,但你可以通過簡單的網頁搜索,找到 大量適用於 iOS 開發的 MVVM 資訊。我將在本教程中解釋 MVVM,但我想讓你知道,Apple 似乎仍然停留在 MVC 中 ── 至少到這個時候,Apple 還沒有談論任何其它的整體應用程式設計模式。

初步定義

你將會注意到文中出現 「視圖/控件」(views/controls) 和「視圖控制器」(view controllers),當我說「視圖/控件」時,指的是我們在 iOS 中用來構建用戶介面 (UI) 使用的所有物件。我們都認同,一個視圖表示可以呈現給用戶的東西,比如包含一段文本的 UITextView,而像是 UIButton 的控件是讓某人能夠與應用程式互動的物件。一個 UIView 可以作為我們組織所有其他控件的基本設計表面。所以「視圖/控件」是指 UIView 類別、和其它提供用戶交互的 UI 控件如 UISlider。請記住所有的控件,至少 UIKit 中的所有控件如 UIButtonUISlider,它們都繼承自 UIView

查看 UIKit 中的 UIButton 類別,在 Apple 的文檔頁面中找到 “Inherits From” 連結,查看繼承樹並從葉級層 UIButton 向上查找父類別 UIView。參考下圖,它看起來像這樣:

UIButton-Inheritance

在這個討論中,「視圖/控件」與 UIView 是同義的。

當我介紹「視圖控制器」時,我是指像 UIViewControllerUITableViewControllerUIPageViewController 等類別,這些類別使視圖/控件可用和實用。為了在 iOS 開發環境中討論 MVC 和 MVVM,「視圖/控件與視圖控制器」和 “MVC” 中的 “VC” 和 “MVVM” 中的第二個 “V” 同義。

請記住,每個 UIViewController 都有 view 的實例屬性 (instance property),它是「由 controller 管理的 view」

這個 view 的屬性被宣告如下:

你可以在 Storyboard 中看到 view 屬性與它的 Owner UIViewController 的關係:

View_Property_Of_Controller

這個 view 是個淡藍色的矩形。

Model-View-Controller (MVC)

Apple 仍在 developer library 中保留有關 MVC 的詳細介紹和大量文檔,儘管該公司將此開發指南標記為 “retired”,但當開發者使用 iOS SDK(如 FoundationUIKitCoreGraphics 時,還是可以明顯發現 Apple 仍是偏向 MVC 的。

想想你幾乎每天都會用到的類別,如 UIViewUIViewControllerUITableViewUITableViewController UIPageControlUIPageViewController。在 iOS 中,視圖/控件和視圖控制器之間關係非常緊密。你可以用程式碼刻出整個用戶介面,但這是很愚蠢的,尤其是在大型應用程式中,考慮螢幕佈局 (Adaptive Auto Layout) 時,根本無法管理和擴展編程建置的 UI。

是的,我們偶爾會需要幾行控制 UI 的程式碼,但會是整個 UI 嗎?我傾向將視圖/控件和視圖控制器當作不可分離的實體,封裝在 Storyboard 和相應的 Swift (.swift) 文件中。我的主要觀點是,對於大多數人來說,編程 iOS 時 “MVC” 中的 “VC” 意味著,在處理視圖/控件和視圖控制器時,兩者有巨大的交集。

在瀏覽或搜索 Apple 文檔時,在泛型資料結構方面,你不會看到 “Model” 這個詞來當 MVC 應用程式的模型,這有部分原因是由於對如 classstructenum 等擁有 Swift 和 Objective-C 語法支持,我希望開發人員將這些視為創建數據模型的首選平台。

當你在 Apple 文檔中遇到 “model” 這個詞時,它通常與非常特定的數據結構應用 Data Organization Tools 相關,例如 VNCoreMLModel。當然,Apple 提供了 Core Data「一個用來管理應用程式中 model layer 物件的框架」,但是它往往需要花費很多工作來啟動和運行,並且使用起來可能有些笨拙。

由於 Apple 一直以來都偏向 MVC,讓我們看看這個設計模式的定義

⋯⋯ MVC 設計模式將一個應用程式劃分成三組元件:模型 (Model)、視圖 (View) 和控制器 (Controller)。 MVC 模式定義了這三組元件在應用程式中扮演的角色和溝通方向。在設計一個應用程式時,最重要的一個步驟是選擇或創建屬於這三組元件之一的自定義類別。三組元件都會由抽象邊界來分隔開,並跨越邊界與其他元件溝通。

模型元件封裝數據和基本行為
模型元件代表特別和專業知識,它們保存應用程式的數據,並定義操作該數據的邏輯。一個設計得好的 MVC 應用程式會將所有重要數據封裝在模型元件中 ⋯⋯

視圖元件向用戶展示數據
視圖元件知道如何展示、甚至允許用戶去編輯應用程式模型的數據。視圖不應負責儲存正在展示的數據。

控制器元件把模型聯繫到視圖
控制器元件就是應用程式視圖和模型的中介。控制器通常負責確保視圖可以取得需要顯示的模型,並充當管道,讓視圖通過管道了解模型的變化。控制器元件還可以執行應用程式的設置和協調任務,並管理其他元件的生命週期。 …

Massive-View-Controller (MVC?)

MVC 雖然在理論上不錯,但它在 iOS 和 Xcode 的背景下通常無法實現,我一次又一次看到開發人員將大部分應用程式的模型(數據)和業務邏輯程式碼放入視圖控制器,所以才被人取笑是 “Massive-View-Controller” 設計模式。但這是一個自然發展的趨勢,因為視圖控制器在應用程式生命週期中扮演著一個最重要的角色:與用戶的互動。

用來對抗複雜性的最佳方法就是分而治之。透過區分權責思維模式,編寫小而邏輯集中組織的程式碼,就是控制複雜性的重要條件,正如下文將看到的 Swift extension 語法結構一樣。

請記住,還有其它非常強大的工具來控制複雜性,如 protocol-oriented programming (POP)(請參閱此處)、object-oriented programming (OOP)error checkingdelegation 以及 property observers 等。

過於肥大的視圖控制器將很難作測試,因為它們有很多屬性,這會帶來不可估量的狀態。如果它們包含許多將業務邏輯與 UI 組件混合的函式,那麼開發有效的測試協定 (testing protocols) 就會非常困難。

過去,Apple 的 Xcode 都鼓勵使用 MVC 模式(我喜歡稱之為「VC 減去 M」),以下是在創建新專案時普遍使用的 iOS Single View App 模板生成的默認文件和目錄結構:

Single_View_App_Template

請注意,圖片中 Xcode 模板文件沒有包含表示 data model 的 classstruct。MVC 的 Model 部分似乎被假定了,並且幾乎都會被有經驗的開發人員視為理所當然,但是新進開發者很可能就會遺忘。如果能由我作主,那麼當使用 Single View App 或其它專案模板創建新項目時,我會重寫 Xcode 以包含一個 “Model.swift” 文件,我的 Model.swift 文件看起來應像這樣:

或是這樣:

兩個 Model.swift 文件版本都由新創建的專案進行編譯,所以 Xcode 建置的理想 iOS 應用模板看起來像這樣:

MVC_Single_View_App_Template

Model-View-ViewModel (MVVM)

我敢打賭,Model-View-ViewModel (MVVM) 設計模式與 MVC 的意圖差不多,MVVM 比 MVC 模型多添加了一個組件,稱為 ViewModel(視圖模型),它可以是 classstruct,但通常會是一個 class,因此可以在程式碼中傳遞同一個物件的 reference,而 ViewModel 就位於視圖控制器和模型之間。

記得我之前提到,為了在 iOS 開發環境中討論 MVC 和 MVVM,「視圖/控件和視圖控制器」與 “MVC” 中的 “VC”、和 “MVVM” 中的第二個子母 “V” 同義。我們將視圖/控件和視圖控制器當作不可分離的實體,封裝在 Storyboard 和相應的 Swift(.swift) 文件中。

讓我們透過定義每個組成字母 M、V、VM 來理解這種模式。

M 代表「模式」(Model) ── 這是一種以最簡單的格式儲存特定資訊(數據)的結構。藉由保持原始狀態,我們保有它的便攜性和可重用性。例如,我們可以將電話號碼存為 10 位數字或字符,將其保留為 ViewModel,把原始 “2125514701” 轉換為 “(212) 551-4701″、或 “212-551-4701″、或 “212.551.4701” 之類的東西,視圖/視圖控制器可以顯示它從 ViewModel 獲得的電話號碼資訊,如果收到撥打國際號碼要求,會將這些處理細節留給 ViewModel。

V 代表「視圖」(View) ── 我想無須再次討論「視圖/控件和視圖控制器」,請參考我在初步定義的討論。

VM 代表 ViewModel ── 如果由我作主,這是我將添加到 MVC 的新區塊,ViewModel 位於模型和視圖/視圖控制器之間,ViewModel 將模型中的數據轉換為可讀格式,可由視圖控制器在視圖中呈現。ViewModel 還可以利用 property observer 或 key-value observing (KVO) 處理用戶對視圖/視圖控制器呈現數據的更新,視圖數據的更新不會直接進入模型,而是視圖控制器先與 ViewModel 互動,然後與模型交戶。

具體來說,ViewModel 可以使用 DateFormatter 將模型中日期的原始表示,轉換為用戶在視圖上可讀的格式。

總括而言:

  • 視圖/視圖控制器將模型數據呈現給用戶,並且可以允許更新該模型數據。視圖/視圖控制器不會直接與模型溝通,視圖/視圖控制器只能通過 ViewModel 與模型間接溝通。
  • 視圖只能與視圖控制器交互。在 UI 中,應用程式數據完全由 ViewModel 提供給視圖控制器,並透過視圖顯示,而且對視圖中的數據所做的任何更改都必須先傳遞給視圖控制器,該控制器僅 與 ViewModel 溝通
  • ViewModel 是視圖/視圖控制器和模型之間的中介,它是單一的管道、一個守門者,數據通過它從模型流向視圖/視圖控制器,反之亦然。
  • 模型是一個僅用於儲存域名/應用程式特定資訊的數據結構,只有 ViewModel 可與模型對話,也只有 ViewModel 會與視圖/視圖控制器進行互動。

本文不會介紹到的東西

單純讓讀者理解 MVC 和 MVVM 的差異就已經很困難了,所以我不想在本教程中混進太多進階的主題。例如,我本可以展示用戶如何編輯一個被視圖控制器控制的 UITextField,而這個視圖控制器可以在 ViewModel 中設置一個屬性,並且透過 property observer 更新模型⋯⋯ 但我沒有有本文提及這些;另一方面,我也可以講述 ViewModel 如何監視 Model 的變化,然後更新視圖控制器來更新視圖⋯⋯ 但我也沒有提到;我更可以向你展示放在視圖控制器中的 KVO 程式碼⋯⋯ 但我都沒有提及。Swift 4 目前 KVO 實作的方法看起來很笨拙、與 Objective-C 密切相關,它未來可能會有變動,也可能漸漸不受重視。

關於 MVVM 的爭議

一如以往,大眾對 MVVM 設計模式亦有不同的聲音,而我是一個實用主義者,不是教條或專制主義,當我聽到別人說「永遠不會把 UIKit import 到 ViewModel Swift 檔案內,又或是「絕不會將 closure 從視圖控制器傳遞給 ViewModel」時,我都會說:「當我遇到這種情況時,我會進行評估並選擇最簡單和安全的方法」。你會看到以下兩個警告的例子。

我經常看到對於 MVVM 的一個爭論點,就是設計模式中用於下載檔案的 networking code 位置。對我來說答案很簡單:把用於下載檔案的 networking code 放在 ViewModel 內。模型儲存原始數據,但它不知道檔案需要透過何種傳輸方式(如 HTTPS)下載,也不知道顯示在哪些特定的控件(如 UIImageView);相反,ViewModel 層就更接近展示層 (presentation layer),負責處理數據,並且應該知道有關文件的儲存位置、以及如何獲取文件。

只是,到底哪裡是 MVVM 應用程式中下載文件的「最佳」位置?是否每次都會有一個「正確」答案?

範例程式碼

為了展示 MVC 和 MVVM,我創建一個範例應用程式,幫助對天文學感興趣的人觀看一類名為 “Messier object”(梅西爾天體)的恆星現象,如球狀星團和星雲。這應用程式可以作為在 “Hubble’s Messier Catalog” 內提供通往 NASA 收藏 Messier-type object 的 prototype:

「Charles Messier (1730–1817) 是一位法國天文學家,以 “Catalog of Nebulae and Star Clusters” 一作聞名。Messier 是一個狂熱的彗星獵人,他編制了一個深空天體目錄,以幫助其他彗星愛好者免於浪費時間研究非彗星的物體。」

MVC 應用程式

我構建了一個 prototype 應用程式,作為最終應用程式的概念驗證。我想知道 UI 呈現的樣貌、需要甚麼框架、需要寫多少程式碼、以及怎樣的程式碼。最重要的是,我使用了 MVC 設計模式,來看看大家有多容易陷入編寫 “Massive-View-Controller” 程式碼的陷阱,而不是真的在使用 MVC 設計模式。正如前文所指,這類 Massive-VC 應用程式仍然是我遇到新客戶時最常看到的情況。

以下是正在運行的 prototype 應用程式:

prototype_in_action_1

大部分人都應該明白我在做什麼。我已經連接了一個 UITableView 來顯示幾行數據,點擊每一行就會執行一個 segue,來顯示一個詳細頁面,它可以顯示多於一行的資訊。我將主要的 UIViewController 子類別嵌入到 UINavigationViewController 中,讓 segue 將詳細頁面視圖控制器推到螢幕上,用戶可方便地使用 “< Back” 按鈕返回到 UITableView

我們來看看這個 prototype 應用程式的專案結構,你應該可以立即看到一些明顯的缺陷:

Apple_MVC_Xcode_Project

記得我說我喜歡稱 Xcode MVC 模式為「VC 減去 M」嗎?我們看看主要的 UIViewController 程式碼再來討論。

我將一步一步講述程式碼,下文亦會逐步解釋。因此,當閱讀註釋中的步驟時,請參閱下面程式碼中的相同步驟。

MVC 實作程式碼

談談 MVC 程式碼中的註釋

如下文所指,程式碼中大多數下了註釋的問題都與違反分治 (divide and conquer) 、關注點分離 (separation of concerns)、或單一責任原則 (single responsibility principle) 相關,你應該查找並研究這些術語。

#1 ── UIViewController 為什麼在這個 ViewController.swift 文件中指定 UITableViewDelegateUITableViewDataSource 協定?如此一來,這個 UIViewController 子類別不僅難以閱讀,而且這些協定的所有屬性和/或方法一致性要求必須在該文件中實現,因而導致程式碼膨脹。如果 UIViewController 類別需要更多的協定呢?我見過視圖控制器採用多達 6 到 8 種不同協定的例子。我們將在下面的 MVVM 範例中看到一個很好的解決方案。

#2 ── 儘管 MVC 模式指定 “M”(模型)是分開的,但許多開發者只是將整個數據模型推送到主視圖控制器子類別中,這違反了關注點分離並導致程式碼膨脹,特別是模型複雜的時候。

#3 ── 這些所有的 callbacks,包括 viewDidLoadviewDidLayoutSubviewsviewDidAppear 等,都應該放在視圖控制器子類別中。一個視圖控制器已經有很多職責,不應該再放置不屬於視圖控制器的程式碼,比如業務邏輯、補助函數 (Helper Functions)、數據結構、數據模型邏輯等。當一個視圖控制器多了這些額外的程式碼,就會變得難以讀取、維護、優化、支持,是時候面對專注點分離了。

#4 ── 由於我已經構建了許多提供 Interactive Help 的應用程式,因此我使用 OOP 創建了自己的 help class 層次結構。這樣它就可重複使用並易於維護,並提供索引、搜索功能和目錄。只要把應用程式/特定領域的資料填充到模型裡面,就可以在不同的應用程式中使用它。至少你可以將你的 help-handling 入口方法添加到不同的 Swift 文件中,比如 help.swift

#5 ── prepare(for:sender:) 的實例在這裡完全適用。移除所有違反關注點分離原則的程式碼,就會有足夠的空間放下 prepare(for:sender:) 的實例。

#6 ── 為什麼我們需要這些方法來遵循 UITableViewDelegateUITableViewDataSource 協定?這違反了關注點分離,我們將在下面的 MVVM 範例中看到一個很好的解決方案。

MVVM 應用程式

MVVM

我用 MVVM 模式構建了一個天文學 Messier object 的探索應用程式,我真的遵循了 MVVM 的關注點分離原則,有一個模型、視圖/視圖控制器、一個 ViewModel,我們閱讀並討論下列程式碼,以逐個認識 MVVM 的元件。

以下是正在運行的 prototype 應用程式:

App Demo

大部分讀者應該明白我在做什麼,就如同我在前文 「MVC 應用程式」 中第三段給的解釋一樣,只是這些程式碼是使用 MVVM 建構的。

讓我們來看看這個 prototype 應用程式的專案架構,應該可以明顯看到許多比 Massive-VC app 程式碼進步的地方:

MVVM Project Structure

我將逐步解釋程式碼,而這些步驟也可對應下方程式碼中的註釋。因此,當閱讀註釋中的步驟時,請參閱下面程式碼中的相同步驟。

MVVM 程式碼範例:模型

模型是僅用於存放特定應用程式/領域資訊的數據結構,只有 ViewModel 能與視圖/視圖控制器對話,同樣地,也只有 ViewModel 會與模型進行交談。

這個模型嚴格按照原始數據類型進行構建,以實現可移植性。應用程式介面可能會改變,模型可能用於不同的應用程式,我希望這個模型可以長久使用。我對於程式碼唯一的評論是,我不是從 NASA JSONRSS feed 中讀取數據,而是寫死這個模型數據。MVVM 已經夠難理解了,無需在解析某些結構化文件格式時仔細查看一堆輔助程式碼。想了解更多關於使用 NASA 數據的資料,請參閱此連結

MVVM 程式碼範例:The ViewModel

ViewModel 是視圖/視圖控制器和模型之間的中介,它是單一的管道、一個守門者,數據通過它從模型流向視圖/視圖控制器,反之亦然。

#1 ── 在 ViewModel 中導入 UIKit 會是個問題嗎?我扮演魔鬼的擁護者,我很好奇讀者如何看待我在 ViewModel 中使用 UIFont 的情況,我會不會太接近 UI?許多人說,ViewModel 的主要功能就是提供呈現邏輯,所以「我應該留下還是拿掉它?」

#2 ── 請注意,我定義了一個閉包型別,所以我可以在 MVVM 各層之間傳遞程式碼,這樣我是否違反了關注點分離原則?我對此表示懷疑。閉包非常方便且易於控制。

#3 ── 實際上,應用程式的數據來源是 ViewModel。

#4 ── 我選擇了一個類別 (class) 為 ViewModel 型別,所以我可以使用 reference 特性,並在應用程式內傳遞模型。

#5 ── 我使用一些私人屬性來準備在 UI 中展示的數據。例如,我將模型中的 hyperlink 存為 String,但將其轉換為 URL 以下載文件。

#6 ── 數據只能通過 public getters 進行呈現,直接訪問模型是不允許的。有些 getters 會對數據進行預先處理以供展示。

#7 ── 在 ViewModel 內使用 UIKitUIFont,這個「有爭議」的處理方式請參閱#1

#8 ── 將@escapingcompletion handler 傳送到 ViewModel 會有問題嗎?我應該使用 KVO 或是委任 (delegation)?這裡我做的就是獲取一些 NSData/Data。如果對應用程式進行了改動,那麼我可能不得不更改委任程式碼,因為閉包允許創建幾乎任何類型的程式碼塊,更可隨時隨地調用。它們是獨立的,但同時亦可以從定義的環境中取得變數和常數。它們亦可以被分配給屬性、變數和/或常數,並作為參數傳遞給函數/方法。

MVVM 程式碼範例:視圖控制器程式碼

應用程式的主視圖控制器變得非常簡單,程式碼只有一頁,我是如何做到的?下面就是 ViewController.swift 的程式碼:

#1 ── 透過使用 Swift extension 語法,我能夠移動主視圖控制器的程式碼,以符合 UITableViewDelegateUITableViewDataSource 協定。這個技巧非常簡單。你可能會說使用Swift extension 與 MVVM 無關,但我不認同,因為設計模式的目的就是幫助開發者達到專注點分離,將程式碼分解為更小、更易於管理、更符合邏輯的組件,請參閱下面應用程式專案中的下兩個文件。

#2 ── 應用程式的數據源不是模型,而是 ViewModel。當前選定代表 Messier object 的 TableViewCell,其 ViewModel 數據通過 segue 傳遞給 Detail View Controller,然後用戶就可以在大螢幕上看到 Messier object。

這是主視圖控制器的程式碼,在 VC-Extension + TableViewDelegate.swift 中遵循 UITableViewDelegate 協定:

我在 didSelectRowAt 中執行的操作是不選擇一個指定的 Table View Row,這樣一來,它被選中時將移除 highlight 效果,讓用戶可以知道他們選擇了哪些 Row;否則,當 Row 一旦被點擊過,它將保持 highlight 顯示效果。

這是主視圖控制器的程式碼,在 VC-Extension + TableViewDelegate.swift 中遵循 UITableViewDataSource 協定:

#1 – 應用程式的數據源不是模型,而是 ViewModel。

大多數讀者應該對這些程式碼很熟悉,我將存在 ViewModel 中的每個 Messier object 與 UITableViewCell 中的每個默認 prototype cell UI 元素連接起來。

來看看最後一個視圖控制器的程式碼,我們將在 DetailViewController.swift 中查看 DetailViewController class:

#1 ── 定義一個用於在圖像下載後更新 UIImageView 的閉包(completion block)實例。

#2 ── 在背景線程上執行下載動作,但 UI 更新必須在主線程上執行(在 completion block 中)。

#3 ── 為 Messier 圖片設置動畫(在 completion block 中)。

#4 ── 完成圖片下載後,停止並隱藏 activity spinner(在 completion block 中)。

#5 ── 當圖片將開始在背景進行下載時,啟動並顯示 activity spinner。

#6 ── 使用 user 選擇查看的 Messier object 資訊更新 UI。

#7 ── 在背景啟動圖片下載作業。

#8 ── 確保 UITextView 顯示 Messier object 敘述的起始文本。

MVVM 範例程式碼:視圖/視圖控制器

快速瀏覽一下我的 Storyboard。我已經連接了一個 UITableView 來顯示幾行 Messier object 數據,點擊每一個 row 將執行一個 segue 來顯示一個詳細資訊頁面,頁面將顯示多於一行的資訊,還包括 Messier object 的圖片。圖片將在背景下載,因為它們是非常大的 multi-megabyte 檔案。我將主要的 UIViewController 子類別嵌入到 UINavigationViewController 中,以便 segueUIViewController 子類別的詳細頁面顯示到螢幕上,然後用戶可以使用內置的 “< Back” 按鈕返回到 UITableView

這是 Main.storyboard 檔案的螢幕截圖:

Main_storyboard

結論

在開發軟件時,我們真的是在控制混亂 ── 至少是在嘗試控制混亂。隨著開發人員向專案添加更多程式碼(variables、constants、structures、enumerations、classes、protocols、conditionals、repetitive constructs 等),軟體複雜性指數式增長。看看這裡的圖表,你就會發現當一個軟體應用程式碼接近六千行,複雜性會逼近無限 ── 不可知、完全不可預測、不可控制。

據估計,Windows 7 包含約四千萬行程式碼,而 macOS 10.4(Tiger)包含約八千五百萬行程式碼,預計這些系統可能表現的行為總量在計算上是不可能的。記得我提到過「指數式」,即復雜性接近無限時,任何具有四千或八千五百萬行程式碼的應用程式都是無限複雜,沒有人能夠知道這些應用程式可以展示的每種可能狀態或行為,我們只能盡力控制混亂。

請記住,即使你和團隊處理的那些「較小」應用程式,也可以輕易達到無限複雜。

那麼工程師可以怎樣做?繼續學習與設計模式有關的文章、看看幫助控制軟體複雜性的其他工具、查詢術語「軟體複雜性 (software complexity)」並加以研究。

請記住,世上沒有完美解決方案。不要與提倡烏托邦或主張 all-or-nothing 的人爭論。我們只是人類,都會隨著時間一直在成長,盡力而為吧。

譯者簡介:陳奕先-過去為平面財經記者,專跑產業新聞,2015 年起跨進軟體開發世界,希望在不同領域中培養新的視野,於新創學校 ALPHA Camp 畢業後,積極投入 iOS 程式開發,目前任職於國內電商公司。聯絡方式:電郵 [email protected]

FB : https://www.facebook.com/yishen.chen.54
Twitter : https://twitter.com/YeEeEsS

原文Introduction to MVVM: Refactoring a MVC App Using the MVVM Design Pattern


熱愛寫作的多產作家,亦是軟體工程師、設計師、和開發員。最近專注於 Objective-C 和 Swift 的 iOS 手機 App 開發。但對於 C#、C++、.NET、JavaScript、HTML、CSS、jQuery、SQL Server、MySQL、Oracle、Agile、Test Driven Development、Git、Continuous Integration、Responsive Web Design 等。

blog comments powered by Disqus
訂閲電子報

訂閲電子報

AppCoda致力於發佈優質iOS程式教學,你不必每天上站,輸入你的電子郵件地址訂閱網站的最新教學文章。每當有新文章發佈,我們會使用電子郵件通知你。

已收你的指示。請你檢查你的電郵,我們已寄出一封認證信,點擊信中鏈結才算完成訂閱。

Shares
Share This