關於iOS中MVC和MVVM的區別和聯繫

MVC 的歷史

MVC,全稱是 Model View Controller,是模型 (model)-視圖(view)-控制器 (controller) 的縮寫。它表示的是一種常見的客戶端軟件開發框架。

MVC的概念最先出如今二十世紀八十年代的 施樂帕克 實驗室中(對,就是那個發明圖形用戶界面和鼠標的實驗室),當時施樂帕克爲 Smalltalk 發明了這種軟件設計模式。

如今,MVC 已經成爲主流的客戶端編程框架,在 iOS 開發中,系統爲咱們實現好了公共的視圖類:UIView,和控制器類:UIViewController。大多數時候,咱們都須要繼承這些類來實現咱們的程序邏輯,所以,咱們幾乎逃避不開 MVC 這種設計模式。

可是,幾十年過去了,咱們對於 MVC 這種設計模式真的用得好嗎?其實不是的,MVC 這種分層方式雖然清楚,可是若是使用不當,極可能讓大量代碼都集中在 Controller 之中,讓 MVC 模式變成了 Massive View Controller 模式。

Controller的臃腫問題何解?

不少人試圖解決 MVC這種架構下 Controller 比較臃腫的問題。我還記得半年前 InfoQ 搞了一次移動座談會,當時 BeeFramework 和 Samurai-Native 的做者 老郭 問了我一句話:「什麼樣的內容才應該放到 Controller 中?」。可是當時由於時間不夠,我沒能展開個人觀點,此次正好在這裏好好談談我對於這個問題的想法。

咱們來看看 MVC 這種架構的特色。其實設計模式不少時候是爲了 Don't repeat yourself 原則來作的,該原則要求可以複用的代碼要儘可能複用,來保證重用。在 MVC 這種設計模式中,咱們發現 View 和 Model 都是符合這種原則的。

對於 View 來講,你若是抽象得好,那麼一個 App 的動畫效果能夠很方便地移植到別的 App 上,而 Github 上也有不少 UI 控件,這些控件都是在 View 層作了很好的封裝設計,使得它可以方便地開源給你們複用。

對於 Model 來講,它實際上是用來存儲業務的數據的,若是作得好,它也能夠方便地複用。好比我當時在作有道雲筆記 iPad 版的時候,咱們就直接和 iOS 版複用了全部的 Model 層的代碼。在創業作猿題庫客戶端時,iOS 和 iPad 版的 Model 層代碼再次被複用上了。固然,由於和業務自己的數據意義相關,Model 層的複用大多數是在一個產品內部,不太可能像 View 層那樣開源給社區。

說完View和Model了,那咱們想一想Controller,Controller有多少能夠複用的?咱們寫完了一個 Controller 以後,能夠很方便地複用它嗎?結論是:很是難複用。在某些場景下,咱們可能能夠用addSubViewController 之類的方式複用 Controller,但它的複用場景仍是很是很是少的。

若是咱們可以意識到 Controller 裏面的代碼不便於複用,咱們就能知道什麼代碼應該寫在 Controller 裏面了,那就是那些不能複用的代碼。在我看來,Controller 裏面就只應該存放這些不能複用的代碼,這些代碼包括:

在初始化時,構造相應的 View 和 Model。

監聽 Model 層的事件,將 Model 層的數據傳遞到 View 層。

監聽 View 層的事件,而且將 View 層的事件轉發到 Model 層。

若是 Controller 只有以上的這些代碼,那麼它的邏輯將很是簡單,並且也會很是短。

可是,咱們卻很難作到這一點,由於仍是有不少邏輯咱們不知道寫在哪裏,因而就都寫到了 Controller 中了,那咱們接下來就看看其它邏輯應該寫在哪裏。

如何對 ViewController 瘦身?

objc.io 是一個很是有名的 iOS 開發博客,它上面的第一課 《Lighter View Controllers》 上就講了不少這樣的技巧,咱們先總結一下它裏面的觀點:

UITableView 的 Data Source 分離到另一個類中。

將數據獲取和轉換的邏輯分別到另一個類中。

將拼裝控件的邏輯,分離到另一個類中。

你想明白了嗎?其實 MVC 雖然只有三層,可是它並無限制你只能有三層。因此,咱們能夠將 Controller 裏面過於臃腫的邏輯抽取出來,造成新的可複用模塊或架構層次。

我我的對於邏輯的抽取,有如下總結。

將網絡請求抽象到單獨的類中

新手寫代碼,直接就在 Controller 裏面用 AFNetworking 發一個請求,請求的完數據直接就傳遞給 View。入門一些的同窗,知道把這些請求代碼移到另一個靜態類裏面。可是我以爲還不夠,因此我建議將每個網絡請求直接封裝成類。

把每個網絡請求封裝成對象實際上是使用了設計模式中的 Command 模式,它有如下好處:

將網絡請求與具體的第三方庫依賴隔離,方便之後更換底層的網絡庫。實際上咱們公司的 iOS 客戶端最初是基於 ASIHttpRequest 的,咱們只花了兩天,就很輕鬆地切換到了 AFNetworking。

方便在基類中處理公共邏輯,例如猿題庫的數據版本號信息就統一在基類中處理。

方便在基類中處理緩存邏輯,以及其它一些公共邏輯。

方便作對象的持久化。sql

你們若是感興趣,能夠看咱們公司開源的 iOS 網絡庫:YTKNetwork。它在這種思考的指導下,不但將 Controller 中的代碼瘦身,並且進一步演化和增強,如今它還支持諸如複雜網絡請求管理,斷點續傳,插件機制,JSON 合法性檢查等功能。

這部分代碼從 Controller 中剝離出來後,不但簡化了 Controller 中的邏輯,也達到了網絡層的代碼複用的效果。

將界面的拼裝抽象到專門的類中

新手寫代碼,喜歡在 Controller 中把一個個 UILabel ,UIButton,UITextField 往 self.view 上用 addSubView 方法放。我建議你們能夠用兩種辦法把這些代碼從 Controller 中剝離。

方法一:構造專門的 UIView 的子類,來負責這些控件的拼裝。這是最完全和優雅的方式,不過稍微麻煩一些的是,你須要把這些控件的事件回調先接管,再都一一暴露回 Controller。

方法二:用一個靜態的 Util 類,幫助你作 UIView 的拼裝工做。這種方式稍微作得不太完全,可是比較簡單。

對於一些能複用的 UI 控件,我建議用方法一。若是項目工程比較複雜,我也建議用方法一。若是項目太緊,另外相關項目的代碼量也很少,能夠嘗試方法二。

構造 ViewModel

誰說 MVC 就不能用 ViewModel 的?MVVM 的優勢咱們同樣能夠借鑑。具體作法就是將 ViewController 給 View 傳遞數據這個過程,抽象成構造 ViewModel 的過程。

這樣抽象以後,View 只接受 ViewModel,而 Controller 只須要傳遞 ViewModel 這麼一行代碼。而另外構造 ViewModel 的過程,咱們就能夠移動到另外的類中了。

在具體實踐中,我建議你們專門建立構造 ViewModel 工廠類,參見 工廠模式。另外,也能夠專門將數據存取都抽將到一個 Service 層,由這層來提供 ViewModel 的獲取。

專門構造存儲類

剛剛說到 ViewModel 的構造能夠抽獎到一個 Service 層。與此相應的,數據的存儲也應該由專門的對象來作。在小猿搜題項目中,咱們由一個叫 UserAgent 的類,專門來處理本地數據的存取。

數據存取放在專門的類中,就能夠針對存取作額外的事情了。好比:

對一些熱點數據增長緩存

處理數據遷移相關的邏輯

若是要作得更細,能夠把存儲引擎再抽象出一層。這樣你就能夠方便地切換存儲的底層,例如從 sqlite 切換到 key-value 的存儲引擎等。

經過代碼的抽取,咱們能夠將本來的 MVC 設計模式中的 ViewController 進一步拆分,構造出 網絡請求層、ViewModel 層、Service 層、Storage 層等其它類,來配合 Controller 工做,從而使 Controller 更加簡單,咱們的 App 更容易維護。

另外,不知道你們注意到沒,其實 Controller 層是很是難於測試的,若是咱們可以將 Controller 瘦身,就能夠更方便地寫 Unit Test 來測試各類與界面的無關的邏輯。移動端自動化測試框架都不太成熟,可是將 Controller 的代碼抽取出來,是有助於咱們作測試工做的。編程

MVVM 的歷史

MVVM 是 Model-View-ViewModel 的簡寫。

相對於 MVC 的歷史來講,MVVM 是一個至關新的架構,MVVM 最先於 2005 年被微軟的 WPF 和 Silverlight 的架構師 John Gossman 提出,而且應用在微軟的軟件開發中。當時 MVC 已經被提出了 20 多年了,可見二者出現的年代差異有多大。

MVVM 在使用當中,一般還會利用雙向綁定技術,使得 Model 變化時,ViewModel 會自動更新,而 ViewModel 變化時,View 也會自動變化。因此,MVVM 模式有些時候又被稱做:model-view-binder 模式。

具體在 iOS 中,可使用 KVO 或 Notification 技術達到這種效果。

在使用中,我發現你們對於 MVVM 以及 MVVM 衍生出來的框架(好比 ReactiveCocoa)有一種「敬畏」感。這種「敬畏」感某種程度上就像對神同樣,這主要表如今我沒有聽到你們對於 MVVM 的任何批評。

我感受緣由首先是 MVVM 並無很大程度上普及,你們對於新技術通常都不熟,進而不敢妄加評論。另外,ReactiveCocoa 自己上手的複雜性,也讓不少人感受到這種技術很高深難懂,進而加劇了你們對它的「敬畏」。

MVVM 的做用和問題

MVVM在實際使用中,確實可以使得 Model層和View層解耦,可是若是你須要實現 MVVM中的雙向綁定的話,那麼一般就須要引入更多複雜的框架來實現了。

對此,MVVM的做者John Gossman 的 批評 應該是最爲中肯的。John Gossman 對MVVM的批評主要有兩點:

第一點:數據綁定使得 Bug 很難被調試。你看到界面異常了,有多是你 View 的代碼有 Bug,也多是 Model 的代碼有問題。數據綁定使得一個位置的 Bug 被快速傳遞到別的位置,要定位原始出問題的地方就變得不那麼容易了。

第二點:對於過大的項目,數據綁定須要花費更多的內存。

某種意義上來講,我認爲就是數據綁定使得 MVVM 變得複雜和難用了。可是,這個缺點同時也被不少人認爲是優勢

咱們應該客觀評價 MVVM,可是可是,我忽然想到,我好象只須要一個ViewModel 而已,我徹底能夠簡單地作一個 ViewModel 的工廠類或 Service 類就能夠了,爲何要引入這麼多框架?現有的 MVC 真的有那麼大的問題嗎?

有一些人老是追趕着技術,有什麼新技術無論三七二十一立馬就用,結果被各類坑。

又有一些人,老是擔憂新技術帶來的技術風險,不肯意學習。結果如今還有人在用 MRC 手動管理引用計數。

而我想說,咱們須要保持的是一個擁抱變化的心,以及理性分析的態度。在新技術的面前,不盲從,也不守舊,一切的決策都應該創建在認真分析的基礎上,這樣才能應對技術的變化。

相關文章
相關標籤/搜索