一、MVC
從字面意思來理解,MVC 即 Modal View Controller(模型 視圖 控制器),是 Xerox PARC 在 20 世紀 80 年代為編程語言 Smalltalk-80 發明的一種軟件設計模式,至今已廣泛應用于用戶交互應用程序中。其用意在于將數據與視圖分離開來。在 iOS 開發中 MVC 的機制被使用的淋漓盡致,充分理解 iOS 的 MVC 模式,有助于我們程序的組織合理性。
MVC 的幾個明顯的特征和體現:
View 上面顯示什么東西,取決于 Model。
只要 Model 數據改了,View 的顯示狀態會跟著更改。
Control 負責初始化 Model,并將 Model 傳遞給 View 去解析展示。
1)Modal模型對象:
模型對象封裝了應用程序的數據,并定義操控和處理該數據的邏輯和運算。例如,模型對象可能是表示商品數據 list。用戶在視圖層中所進行的創建或修改數據的操作,通過控制器對象傳達出去,最終會創建或更新模型對象。模型對象更改時(例如通過網絡連接接收到新數據),它通知控制器對象,控制器對象更新相應的視圖對象。
2)View 視圖對象:
視圖對象是應用程序中用戶可以看見的對象。視圖對象知道如何將自己繪制出來,可能對用戶的操作作出響應。視圖對象的主要目的就是顯示來自應用程序模型對象的數據,并使該數據可被編輯。盡管如此,在 MVC 應用程序中,視圖對象通常與模型對象分離。
在iOS應用程序開發中,所有的控件、窗口等都繼承自 UIView,對應 MVC 中的 V。UIView 及其子類主要負責 UI 的實現,而 UIView 所產生的事件都可以采用委托的方式,交給 UIViewController 實現。
3)Controller 控制器對象:
在應用程序的一個或多個視圖對象和一個或多個模型對象之間,控制器對象充當媒介。控制器對象因此是同步管道程序,通過它,視圖對象了解模型對象的更改,反之亦然。控制器對象還可以為應用程序執行設置和協調任務,并管理其他對象的生命周期。
控制器對象解釋在視圖對象中進行的用戶操作,并將新的或更改過的數據傳達給模型對象。模型對象更改時,一個控制器對象會將新的模型數據傳達給視圖對象,以便視圖對象可以顯示它。
對于不同的 UIView,有相應的 UIViewController,對應 MVC 中的 C。例如在 iOS 上常用的 UITableView,它所對應的 Controller 就是UITableViewController。
iOS MVC 示意圖
MVC思維導圖
1)Model 和 View 永遠不能相互通信,只能通過 Controller 傳遞。
2)Controller 可以直接與 Model 對話(讀寫調用 Model),Model 通過 Notification 和 KVO 機制與 Controller 間接通信。
3)Controller 可以直接與 View 對話,通過 outlet,直接操作 View,outlet 直接對應到 View 中的控件,View 通過 action 向 Controller 報告事件的發生(如用戶 Touch 我了)。Controller 是 View 的直接數據源(數據很可能是 Controller 從 Model 中取得并經過加工了)。Controller 是 View 的代理(delegate),以同步 View 與 Controller。
MVC自身不足
1)MVC 在現實應用中的不足:
在 MVC 模式中 view 將用戶交互通知給控制器。view 的控制器通過更新 Model 來反應狀態的改變。Model(通常使用 Key-Value-Observation)通知控制器來更新他們負責的 view。大多數 iOS 應用程序的代碼使用這種方式來組織。
2)愈發笨重的 Controller:
在傳統的 app 中模型數據一般都很簡單,不涉及到復雜的業務數據邏輯處理,客戶端開發受限于它自身運行的的平臺終端,這一點注定使移動端不像 PC 前端那樣能夠處理大量的復雜的業務場景。然而隨著移動平臺的各種深入,我們不得不考慮這個問題。傳統的 Model 數據大多來源于網絡數據,拿到網絡數據后客戶端要做的事情就是將數據直接按照順序畫在界面上。隨著業務的越來越來的深入,我們依賴的 service 服務可能在大多時間無法第一時間滿足客戶端需要的數據需求,移動端愈發的要自行處理一部分邏輯計算操作。這個時間一慣的做法是在控制器中處理,最終導致了控制器成了垃圾箱,越來越不可維護。
控制器 Controller 是 app 的 “膠水代碼”,協調模型和視圖之間的所有交互。控制器負責管理他們所擁有的視圖的視圖層次結構,還要響應視圖的 loading、appearing、disappearing 等等,同時往往也會充滿我們不愿暴露的 Model 的模型邏輯以及不愿暴露給視圖的業務邏輯。這引出了第一個關于 MVC 的問題...
視圖 view 通常是 UIKit 控件(component,這里根據習慣譯為控件)或者編碼定義的 UIKit 控件的集合。進入 .xib 或者 Storyboard 會發現一個 app、Button、Label 都是由這些可視化的和可交互的控件組成。View 不應該直接引用 Model,并且僅僅通過 IBAction 事件引用 controller。業務邏輯很明顯不歸入 view,視圖本身沒有任何業務。
厚重的 View Controller 由于大量的代碼被放進 viewcontroller,導致他們變的相當臃腫。在 iOS 中有的 view controller 里綿延成千上萬行代碼的事并不是前所未見的。這些超重 app 的突出情況包括:厚重的 View Controller 很難維護(由于其龐大的規模);包含幾十個屬性,使他們的狀態難以管理;遵循許多協議(protocol),導致協議的響應代碼和 controller 的邏輯代碼混淆在一起。
厚重的 view controller 很難測試,不管是手動測試或是使用單元測試,因為有太多可能的狀態。將代碼分解成更小的多個模塊通常是件好事。
3)太過于輕量級的 Model:
早期的 Model 層,其實就是如果數據有幾個屬性,就定義幾個屬性,ARC 普及以后我們在 Model 層的實現文件中基本上看不到代碼(無需再手動管理釋放變量,Model 既沒有復雜的業務處理,也沒有對象的構造,基本上 .m 文件中的代碼普遍是空的);同時與控制器的代碼越來厚重形成強烈的反差,這一度讓人不禁對現有的開發設計構思有所懷疑。
4)遺失的網絡邏輯:
蘋果使用的 MVC 的定義是這么說的:所有的對象都可以被歸類為一個 Model,一個 view,或是一個控制器。就這些,那么把網絡代碼放哪里?和一個 API 通信的代碼應該放在哪兒?
你可能試著把它放在 Model 對象里,但是也會很棘手,因為網絡調用應該使用異步,這樣如果一個網絡請求比持有它的 Model 生命周期更長,事情將變的復雜。顯然也不應該把網絡代碼放在 view 里,因此只剩下控制器了。這同樣是個壞主意,因為這加劇了厚重控制器的問題。那么應該放在那里呢?顯然 MVC 的 3 大組件根本沒有適合放這些代碼的地方。
5)較差的可測試性:
MVC 的另一個大問題是,它不鼓勵開發人員編寫單元測試。由于控制器混合了視圖處理邏輯和業務邏輯,分離這些成分的單元測試成了一個艱巨的任務。大多數人選擇忽略這個任務,那就是不做任何測試。
上文提到了控制器可以管理視圖的層次結構;控制器有一個 “view” 屬性,并且可以通過 IBOutlet 訪問視圖的任何子視圖。當有很多 outlet 時這樣做不易于擴展,在某種意義上,最好不要使用子視圖控制器(child view controller)來幫助管理子視圖。在這里有多個模糊的標準,似乎沒有人能完全達成一致。貌似無論如何,view 和對應的 controller 都緊緊的耦合在一起,總之,還是會把它們當成一個組件來對待。Apple 提供的這個組件一度以來在某種程度誤導了大多初學者,初學者將所有的視圖全部拖到 xib 中,連接大量的 IBoutLet 輸出口屬性,都是一些列問題。
二、MVP
從字面意思來理解,MVP即Modal View Presenter(模型 視圖 協調器),MVP實現了Cocoa的MVC的愿景。MVP的協調器Presenter并沒有對ViewController的聲明周期做任何改變,因此View可以很容易的被模擬出來。在Presenter中根本沒有和布局有關的代碼,但是它卻負責更新View的數據和狀態。
MVP 是第一個如何協調整合三個實際上分離的層次的架構模式,既然我們不希望 View 涉及到 Model,那么在顯示的 View Controller(其實就是 View)中處理這種協調的邏輯就是不正確的,因此我們需要在其他地方來做這些事情。例如,我們可以做基于整個 App 范圍內的路由服務,由它來負責執行協調任務,以及 View 到 View 的展示。這個出現并且必須處理的問題不僅僅是在 MVP 模式中,同時也存在于以下集中方案中。
MVC和MVP的區別就是,在MVP中M和V沒有直接通信。
1)MVP模式下的三個特性的分析:
任務均攤 -- 我們將最主要的任務劃分到 Presenter 和 Model,而 View 的功能較少;
可測試性 -- 非常好,由于一個功能簡單的 View 層,所以測試大多數業務邏輯也變得簡單;
易用性 -- 代碼量比 MVC 模式的大,但同時 MVP 的概念卻非常清晰。
2)iOS MVP 示意圖:
MVP
1.就 MVP 而言,UIViewController 的子類實際上就是 Views 并不是 Presenters。這點區別使得這種模式的可測試性得到了極大的提高,付出的代價是開發速度的一些降低,因為必須要做一些手動的數據和事件綁定。
2.還有一些其他形態的 MVP -- 監控控制器的 MVP。這個變體包含了 View 和 Model 之間的直接綁定,但是 Presenter 仍然來管理來自 View 的動作事件,同時也能勝任對 View 的更新。
Mvp
3)規范的MVP設計模式:
1、Model 層應該不僅僅是創建一個數據對象,還應該包含網絡請求,以及數據 SQLite 的 CRUD 操作(比如 iOS 平臺,一般以 FMDB 框架直接操作 sql,或者用 CoreData) 。一般可以將數據對象是否需要緩存設計成一個字段 isCache,或者針對整個項目設計一個開存儲關,決定整個項目是否需要數據緩存。我們常見的新聞類 App,在離線的時候看到的數據,都是做了緩存處理的。比如一些金融類的 App,實時性比較高,是不做緩存的。
2、View 層比較簡單明,就是 View 的一些封裝、重用。在一款精心設計過的 App 里面,應該有很多 View 是可以封裝重用的。比如一些自己的 TableViewCell,自己設計的 Button,一些 View(包含一些子 View,UI 精心設計過,在項目里多處出現的)等等。
3、Presenter 層并不涉及數據對象的網絡請求和 SQLite 操作,只是 Model 層和 View 層的一個橋梁。Presenter 層就不至于太臃腫,容易看懂。一些大的 App,或因為上線時間比較久了,經歷過眾多程序員的修補,或因前期并未做好架構,以至于打開一個類,幾千行的代碼,看著自己都暈。
4)MVP的優勢
模型與視圖完全分離,我們可以修改視圖而不影響模型
可以更高效地使用模型,因為所以的交互都發生在一個地方——Presenter內部
我們可以將一個Presener用于多個視圖,而不需要改變Presenter的邏輯。這個特性非常的有用,因為視圖的變化總是比模型的變化頻繁。
如果我們把邏輯放在Presenter中,那么我們就可以脫離用戶接口來測試這些邏輯(單元測試)
5)MVP的問題
由于對視圖的渲染放在了Presenter中,所以視圖和Persenter的交互會過于頻繁。
還有一點你需要明白,如果Presenter過多地渲染了視圖,往往會使得它與特定的視圖的 聯系過于緊密。一旦視圖需要變更,那么 Presenter也需要變更了。比如說,原本用來呈現Html的Presenter現在也需要用于呈現Pdf了,那么視圖很有可能也需要變更。
三、MVVM
從字面意思來理解,MVVM 即 Modal View ViewModel(模型 視圖 視圖模型)。MVC 是一個用來組織代碼的權威范式,也是構建 iOS App 的標準模式。Apple 甚至是這么說的。在 MVC 下,所有的對象被歸類為一個 model,一個 view,或一個 controller。Model 持有數據,View 顯示與用戶交互的界面,而 View Controller 調解 Model 和 View 之間的交互。然而,隨著模塊的迭代我們越來越發現 MVC 自身存在著很多不足。因此,MVVM 從其他應用而出,在 iOS 中從此我們完全將業務邏輯加以區分并使用這套思想。在 MVVM 中他的設計思路和 MVC 很像。它正式規范了視圖和控制器緊耦合的性質,并引入新的組件 ViewModel。此外,它還有像監管版本的 MVP 那樣的綁定功能,但這個綁定不是在 View 和 Model 之間而是在 View 和 ViewModel 之間。
1)MVVM 模式下的三個特性的分析:
任務均攤 -- MVVM 的 View 要比 MVP 中的 View 承擔的責任多。因為前者通過 ViewModel 的設置綁定來更新狀態,而后者只監聽 Presenter 的事件但并不會對自己有什么更新。
可測試性 -- ViewModel 不知道關于 View 的任何事情,這允許我們可以輕易的測試 ViewModel。同時 View 也可以被測試,但是由于屬于 UIKit 的范疇,對他們的測試通常會被忽略。
易用性 -- 在實際開發中必須把 View 中的事件指向 Presenter 并且手動的來更新 View,如果使用綁定的話,MVVM 代碼量將會小的多。
2)iOS MVVM示意圖:
MVVM
1.在 MVVM 里,view 和 view controller 正式聯系在一起,我們把它們視為一個組件。視圖 view 仍然不能直接引用模型 Model,當然 controller 也不能。相反,他們引用視圖模型 View Model。
2.View Model 是一個放置用戶輸入驗證邏輯,視圖顯示邏輯,發起網絡請求和其他各種各樣的代碼的極好的地方。有一件事情不應歸入 View Model,那就是任何視圖本身的引用。View Model 的概念同時適用于于 iOS 和 OS X(換句話說,不要在 View Model 中使用 #import UIKit.h)。
3.由于展示邏輯(presentation logic)放在了 View Model 中(比如 Model 的值映射到一個格式化的字符串),視圖控制器本身就會不再臃腫。當然你開始使用 MVVM 的最好方式時可以先將一小部分邏輯放入視圖模型,然后當你逐漸習慣于使用這個范式的時候再遷移更多的邏輯到視圖模型中。
使用 MVVM 會輕微的增加代碼量,但總體上減少了代碼的復雜性。
具體可參考以下鏈接:http://www.cocoachina.com/ios/20170717/19884.html、https://blog.csdn.net/watson2017/article/details/85260148