Model-View-ViewModel for iOS [譯]

若是你已經開發一段時間的iOS應用,你必定據說過Model-View-Controller, 即MVC。MVC是構建iOS app的標準模式。然而,最近我已經愈來愈厭倦MVC的一些缺點。在本文,我將重溫一下MVC是什麼,詳述它的缺點,而且告訴你一個新的方式來架構你的app:Model-View-ViewModel。拿出你的流行語bingo card(賓果卡,一種遊戲卡片-譯者注),由於咱們即將進行一次範式轉變。html

Model-View-Controller

Model-View-Controller是一個用來組織代碼的權威範式。Apple甚至是這麼說的。在MVC下,全部的對象被歸類爲一個model,一個view,或一個controller。Model持有數據,View顯示與用戶交互的界面,而view controller調解model和view之間的交互。react

MVC

在上圖中,view將用戶交互通知給controller。view controller經過更新model來反應狀態的改變。model(一般使用Key-Value-Observation)通知controller來更新他們負責的view。大多數iOS應用程序的代碼使用這種方式來組織。ios

模型model的對象一般很是很是的簡單。不少時候,他們就是Core Data managed objects,或者避免使用Core Data,就是其餘流行的數據模型層。根據Apple的文檔,model包括數據和操做數據的業務邏輯。在實踐中,model層每每很是薄,無論怎樣,model層的業務邏輯被拖入了controller。git

視圖view一般是UIKit控件(component,這裏根據習慣譯爲控件)或者編碼定義的UIKit控件的集合。進入.xib或者Storyboard會發現一個app、Button、Label都是由這些可視化的和可交互的控件組成。你懂的。View不該該直接引用model,而且僅僅經過IBAction事件引用controller。業務邏輯很明顯不納入view,視圖自己沒有任何業務。github

還有控制器controller。Controller是app的「膠水代碼」:協調模型和視圖之間的全部交互。控制器負責管理他們所擁有的視圖的視圖層次結構,還要響應視圖的loading、appearing、disappearing等等,同時每每也會充滿咱們不肯暴露的model的模型邏輯以及不肯暴露給視圖的業務邏輯。這引出了第一個關於MVC的問題...編程

厚重的View Controller

因爲大量的代碼被放進view controller,致使他們變的至關臃腫。在iOS中有的view controller裏綿延成千上萬行代碼的事並非前所未見的。這些超重app的突出狀況包括:厚重的View Controller很難維護(因爲其龐大的規模);包含幾十個屬性,使他們的狀態難以管理;遵循許多協議(protocol),致使協議的響應代碼和controller的邏輯代碼混淆在一塊兒。網絡

厚重的view controller很難測試,不論是手動測試或是使用單元測試,由於有太多可能的狀態。將代碼分解成更小的多個模塊一般是件好事。架構

遺失的網絡邏輯

蘋果使用的MVC的定義是這麼說的:全部的對象均可以被歸類爲一個model,一個view,或是一個controller。就這些。那麼把網絡代碼放哪裏?和一個API通訊的代碼應該放在哪兒?app

你可能試着把它放在model對象裏,可是也會很棘手,由於網絡調用應該使用異步,這樣若是一個網絡請求比持有它的model生命週期更長,事情將變的複雜。顯然也不該該把網絡代碼放在view裏,所以只剩下controller了。這一樣是個壞主意,由於這加重了厚重View Controller的問題。異步

那麼應該放在那裏呢?顯然MVC的3大組件根本沒有適合放這些代碼的地方。

較差的可測試性

MVC的另外一個大問題是,它不鼓勵開發人員編寫單元測試。因爲view controller混合了視圖處理邏輯和業務邏輯,分離這些成分的單元測試成了一個艱鉅的任務。大多數人選擇忽略這個任務,那就是不作任何測試。

定義模糊的「Manage」

以前我提到了view controller能夠管理試圖的層次結構;view controller有一個「view」屬性,而且能夠經過IBOutlet訪問視圖的任何子視圖。當有不少outlet時這樣作不易於擴展,在某種意義上,最好不要使用子視圖控制器(child view controller)來幫助管理子視圖(subview)。

要點在哪?驗證用戶輸入的業務邏輯應納入controller仍是model呢?

在這裏有多個模糊的標準,彷佛沒有人能徹底達成一致。貌似不管如何,view和對應的controller都牢牢的耦合在一塊兒,總之,仍是會把它們當成一個組件來對待。

Hey!如今有個點子...

Model-View-ViewModel

在理想的世界裏,MVC也許工做的很好。然而,咱們生活在真實的世界。既然咱們已經詳細說明了MVC在典型場景中的問題,那讓咱們看一看一個可供替換的選擇:Model-View-ViewModel。

MVVM來自微軟,不過不要堅持反對它。MVVM和MVC很像。它正式規範了視圖和控制器緊耦合的性質,並引入新的組件。

MVVM

在MVVM裏,view和view controller正式聯繫在一塊兒,咱們把它們視爲一個組件。視圖view仍然不能直接引用模型model,固然controller也不能。相反,他們引用視圖模型view model。

view model是一個放置用戶輸入驗證邏輯,視圖顯示邏輯,發起網絡請求和其餘各類各樣的代碼的極好的地方。有一件事情不該納入view model,那就是任何視圖自己的引用。view model的概念同時適用於於iOS和OS X。(換句話說,不要在view model中使用 #import UIKit.h)

因爲展現邏輯(presentation logic)放在了view model中(好比model的值映射到一個格式化的字符串),視圖控制器自己就會再也不臃腫。當你開始使用MVVM的最好方式是,能夠先將一小部分邏輯放入視圖模型,而後當你逐漸習慣於使用這個範式的時候再遷移更多的邏輯到視圖模型中。

使用MVVM的iOS app是高度可測試的;由於view model包含了全部的展現邏輯而且不會引用view,因此它能夠經過編程方式充分測試。雖然有衆多的hack技術參與到測試Core Data模型,但使用MVVM寫的app能夠進行充分的單元測試。

以個人經驗,使用MVVM會輕微的增長代碼量,但整體上減小了代碼的複雜性。這是一個划算的交易。

回過頭再來看MVVM的圖示,你會注意到我使用了模糊的動詞「notify」和「update」,而沒有詳細說明該怎麼作。你可使用KVO,就像MVC那樣,但這很快就會變得難以管理。事實上,使用ReactiveCocoa會是更好的方式來組織各個部分。

關於怎麼結合ReactiveCocoa來使用MVVM的信息,能夠閱讀Colin Wheeler的excellent write-up或者看看我寫的開源app。你也能夠閱讀個人關於ReactiveCocoa和MVVM的書.

原文連接

Model-View-ViewModel for iOS by Ash Furrow

延伸閱讀

相關文章
相關標籤/搜索