iOS MVC、MVVM、MVP架構模式淺淺析

聲明:本文不少部分是對王巍App 架構一書的學習筆記,若有侵權,請告知程序員

咱們須要決定在 app 中如何執行下列任務:

構建 — 誰負責構建 model 和 view,以及將二者鏈接起來? 更新 model — 如何處理 view action? 改變 view — 如何將 model 的數據應用到 view 上去? view state — 如何處理導航和其餘一些 model state 之外的狀態?編程

App 的本質是反饋迴路

「View 層和 model 層須要交流。因此,二者之間須要存在鏈接。假設 view 層和 model 層是被清晰地分開,並且不存在沒法解耦的聯結的話,二者之間的通信就須要一些形式的翻譯:」 緩存

V和M交互邏輯.png

「當一個 view action 被送到 model 層時,它會被轉變爲model action (或者說,讓 model 對象執行一個 action 或者進行更新的命令)。這種命令也被叫作一個消息 (特別在當 model 是被 reducer 改變時,咱們會這麼稱呼它)。將 view action 轉變爲 model action 的操做,以及路徑上的其餘邏輯被叫作交互邏輯。」架構

「當 view 依賴於 model 數據時,通知會觸發一個 view 變動,來更改 view 層中的內容。這些通知能夠以多種形式存在:Foundation 中的 Notification,代理,回調,或者是其餘機制,都是能夠的。將 model 通知和數據轉變爲 view 更改的操做,以及路徑上的其餘邏輯被叫作表現邏輯。」app

MVC

「MVC 的核心思想是,controller 層負責將 model 層和 view 層撮合到一塊兒工做。Controller 對另外兩層進行構建和配置,並對 model 對象和 view 對象之間的雙向通信進行協調。因此,在一個 MVC app 中,controller 層是做爲核心來參與造成 app 的反饋迴路的:」框架

「圖中的虛線部分表明運行時的引用,view 層和 model 層都不會直接在代碼中引用 controller。實線部分表明編譯期間的引用,controller 實例知道本身所鏈接的 view 和 model 對象的接口」工具

MVC.png

MVC 中有兩個最多見的問題學習

  • 觀察者模式失效 第一個問題是,model 和 view 的同步可能失效。當圍繞 model 的觀察者模式沒有被完美執行時,這個問題就會發生。常見的錯誤是,在構建 view 時讀取了 model 的值,而沒有對後續的通知進行訂閱。另外一個常見錯誤是在變動 model 的同時去更改 view 層級,這種作法假設了變動的結果,而沒有等待 model 進行通知,若是 model 拒絕了這個變動的話,就會發生錯誤。這類錯誤會使得 view 和 model 不一樣步,奇怪的行爲也隨之而來。
  • 肥大的 View Controller View controller 須要負責處理 view 層 (設置 view 屬性,展現 view 等),可是它同時也負責 controller 層的任務 (觀察 model 以及更新 view),最後,它還要負責 model 層 (獲取數據,對其變形或者處理)。結合它在架構中的中心角色,這使得咱們很容易在不經意間把全部的職責都賦予 view controller,從而迅速讓程序變得難以管理。

MVC構建測試

  1. 構建 App 對象負責建立最頂層的 view controller,這個 view controller 將加載 view,而且知道應該從 model 中獲取哪些數據,而後把它們顯示出來。Controller 要麼顯式地建立和持有 model 層,要麼經過一個延遲建立的 model 單例來獲取 model。在多文檔配置中,model 層由更低層的像是 UIDocument 或 NSDocument 所擁有。那些和 view 相關的單個 model 對象,一般會被 controller 所引用並緩存下來。
  2. 更改 Model 在 MVC 中,controller 主要經過 target/action 機制和 (由 storyboard 或者代碼進行設置的) delegate 來接收 view 事件。Controller 知道本身所鏈接的 view,可是 view 在編譯期間卻沒有關於 controller 接口的信息。當一個 view 事件到達時,controller 有能力改變自身的內部狀態,更改 model,或者直接改變 view 層級。
  3. 更改 View」 「在咱們所理解的 MVC 中,當一個更改 model 的 view action 發生時,controller 不該該直接去操做 view 層級。正確的作法是,controller 去訂閱 model 通知,而且在當通知到達時再更改 view 層級。這樣一來,數據流就能夠單向進行:view action 被轉變爲 model 變動,而後 model 發送通知,這個通知最後被轉爲 view 變動。」
  4. View State View state 能夠按須要被 store 在 view 或者 controller 的屬性中。相對於影響 model 的 view action,那些隻影響 view 或 controller 狀態的 action 則不須要經過 model 進行傳遞。對於 view state 的存儲,能夠結合使用 storyboard 和 UIStateRestoring 來進行實現,storyboard 負責記錄活躍的 controller 層級,「而 UIStateRestoring 負責從 controller 和 view 中讀取數據。」

MVVM

「MVVM 構建的方式和 MVC 的模式很類似:controller 層充分了解程序的結構,它使用這些認知來對全部部件進行構建和鏈接。 MVVM 與 MVC 最大的區別可能在於 view-model 中對響應式編程的使用了,它被用來描述一系列的轉換和依賴關係。經過使用響應式編程來清晰地描述 model 對象與顯示值之間的關係,爲咱們從整體上理解應用中的依賴關係提供了重要的指導。 具體主要有三個不一樣:翻譯

  1. 必須建立 view-model。
  2. 必須創建起 view-model 和 view 之間的綁定。
  3. Model 由 view-model 擁有,而不是由 controller 所擁有。」

「MVVM 一般要求 controller 必須很是簡單 (甚至簡單到無需考慮)。另外,controller 必須儘量地使用庫提供的綁定方法。在這樣的規則的保證下,理想狀況中咱們就不須要測試 controller 了,由於它沒有包含咱們本身的任何邏輯。究竟應該在 controller 中留存多少邏輯,根本上來講是掌握在程序員手上的。」

「MVVM 經過將 model 觀察的代碼以及其餘顯示和交互邏輯移動到圍繞着數據流構建的隔離的類中,解決了 MVC controller 裏不規則的狀態交互所帶來的有關問題。由於這是 MVC 中最顯著的問題,並且會隨着 Cocoa controller 的增大而惡化,這個變化在很大程度上緩解了 MVC 中 controller 肥大的問題。可是還有其餘一些因素會使得 controller (以及 view-model) 變大,因此爲了可持續發展,重構依然仍是有須要的」

MVVM.png

MVVM的構建

  1. 構建 和 MVC 不一樣的是,view controller 再也不直接爲每一個 view 獲取和準備數據,它會把這項工做交給 view-model。View controller 在建立的時候會一併建立 view-model,而且將每一個 view 綁定到 view-model 所暴露出的相應屬性上去。
  2. 更改 Model 在 MVVM 中,view controller 接收 view 事件的方式和 MVC 中同樣 (在 view 和 view controller 之間創建鏈接的方式也相同)。不過,當一個 view 事件到達時,view controller 不會去改變自身的內部狀態、view state、或者是 model。相對地,它當即調用 view-model 上的方法,再由 view-model 改變內部狀態或者 model。
  3. 更改 View 和 MVC 不一樣,view controller 不監聽 model。View-model 將負責觀察 model,並將 model 的通知轉變爲 view controller 能夠理解的形式。View controller 訂閱 view-model 的變動,這一般經過一個響應式編程框架來完成,但也可使用任意其餘的觀察機制。當一個 view-model 事件來到時,由 view controller 去更改 view 層級。 爲了實現單向數據流,view-model 老是應該將變動 model 的 view action 發送給 model,而且僅僅在 model 變化實際發生以後再通知相關的觀察者。
  4. View State View state 要麼存在於 view 自身之中,要麼存在於 view-model 裏。和 MVC 不一樣,view controller 中不存在任何 view state。View-model 中的 view state 的變動,會被 controller 觀察到,不過 controller 沒法區分 model 的通知和 view state 變動的通知。當使用協調器時,view controller 層級將由協調器進行管理。
  5. 測試 由於 view-model 和 view 層與 controller 層是解耦合的,因此可使用接口測試來測試 view-model,而不須要像 MVC 裏那樣使用集成測試。接口測試要比集成測試簡單得多,由於不須要爲它們創建完整的組件層次結構。 爲了讓接口測試儘量覆蓋更多的範圍,view controller 應當儘量簡單,可是那些沒有被移出 view controller 的部分仍然須要單獨進行測試。在咱們的實現中,這部份內容包括與協調器的交互,以及初始時負責建立工做的代碼。」

關於view-model

  • 實現狀態恢復數據 在狀態恢復的方法上,MVVM 中爲各個 controller 所存儲的數據來源於 view-model,而非像 MVC 那樣來源於 controller 自己,除此以外,二者所使用的策略大抵相同。

  • View-model 雖然名字裏既有 view 又有 model,可是它所扮演的實際上是徹徹底底的相似 controller 的角色。

  • View-model 從 view controller 和 view 中獨立出來,也能夠被單獨測試。一樣,view controller 也再也不擁有內部的 view state,這些狀態也被移動到了 view-model 中。在 MVC 中 view controller 的雙重角色 (既做爲 view 層級的一部分,又負責協調 view 和 model 之間的交互),減小到了單一角色 (view controller 僅僅只是 view 層級的一部分)。

  • 「View-model 在編譯期間不包含對 view 或者 controller 的引用。它暴露出一系列屬性,用來描述每一個 view 在顯示時應有的值。把一系列變換運用到底層的 model 對象後,就能獲得這些最終能夠直接設置到 view 上的值。實際將這些值設置到 view 上的工做,則由預先創建的綁定來完成,綁定會保證當這些顯示值發生變化時,把它設定到對應的 view 上去。響應式編程是用來表達這類聲明和變換關係的很好的工具,因此它天生就適合 (雖然說不是嚴格必要) 被用來處理 view-model。在不少時候,整個 view-model 均可以用響應式編程綁定的方式,以聲明式的形式進行表達。

  • 「在理論上,由於 view-model 不包含對 view 層的引用,因此它是獨立於 app 框架的,這讓對於 view-model 的測試也能夠獨立於 app 框架。」

MVVM-C

「在理論上,由於 view-model 不包含對 view 層的引用,因此它是獨立於 app 框架的,這讓對於 view-model 的測試也能夠獨立於 app 框架。

因爲 view-model 是和場景耦合的,咱們還須要一個可以在場景間切換時提供邏輯的對象。在 MVVM-C 中,這個對象叫作協調器 (coordinator)。協調器持有對 model 層的引用,而且瞭解 view controller 樹的結構,這樣,它可以爲每一個場景的 view-model 提供所須要的 model 對象。」

和 MVC 不一樣,MVVM-C 中的 view controller 歷來都不會直接引用其餘的 view controller (因此,也不會引用其餘的 view-model)。View controller 經過 delegate 的機制,將 view action 的信息告訴協調器。協調器據此顯示新的 view controller 並設置它們的 model 數據。換句話說,view controller 的層級是由協調器進行管理的,而不是由 view controller 來決定的。若是咱們忽略掉協調器,那麼這張圖表就很像 MVC 了,只不過在 view controller 和 model 之間加入了一個階段。MVVM 將以前在 view controller 中的大部分工做轉移到了 view-model 中,可是要注意,view-model 並不會在編譯時擁有對 view controller 的引用。

「初步印象來講,由於 MVVM-C 加入了額外的一層來進行管理,看起來是比 Cocoa MVC 模式更加複雜。不過,在實現的層級,若是你可以始終如一地貫徹這個模式,代碼會變得更簡單一些。啊,這裏說的簡單並不意味着容易,只有當你對常見的響應式代碼變形熟悉之後,纔不會對書寫代碼感到無從下手,纔不會對調試問題感到懊惱沮喪。不過,從使人高興的一面來講,精心設計的數據管道一般不容易產生錯誤,在長期來看維護也更容易一些。」

MVVM-C.png

「iOS 中的協調器是一種頗有用的模式,由於管理 view controller 層級是一件很是重要的事情。協調器在本質上並無和 MVVM 綁定,它也能被使用在 MVC 或者其餘模式上。」

協調器爲每一個 controller 的 view-model 設置初始的 model 對象。 View-model 將設定值和其餘 model 數據及觀察量進行合併。 View-model 將數據變形爲 view 所須要的精確的格式。 Controller 將準備好的值綁定到各個 view 上去。

MVP

對於Controller層過於臃腫的問題,MVP模式則能較好地解決這個問題——既然UIViewController和UIView是耦合的,索性把這二者都歸爲View層,業務邏輯則獨立存在於Presenter層,Model層保持不變。下圖比較清除得展現了MVP模式的結構

屏幕快照 2018-10-04 00.09.48.png

MVC-VS

MVC+VS.png

MAVB

「model 適配器 - view 綁定器 (ModelAdapter-ViewBinder, MAVB)」

MAVB.png

其餘模式

Elm 架構 (TEA)、VIPER、Riblets

響應式編程

「響應式編程是一種用來交流變動的工具,不過和通知或者 KVO 不一樣的是,它專一於在源和目標之間進行變形,讓邏輯能夠在部件之間傳輸信息的同時得以表達。」 「響應式編程是一種用來描述數據源和數據消費端之間數據流動的模式」 「數據變形的部分是響應式編程所能帶來的最大優點,但同時它也是學習曲線最爲陡峭的部分。」

相關文章
相關標籤/搜索