一、概述
① 應用架構
- App 架構是軟體設計的一個分支,它關心的是如何設計一個 App 的結構,具體來說,它關注于兩個方面:如何將 App 分解為不同的介面和概念層次部件,以及這些部件之間和自身的不同操作中 所使用的控制流和資料流路徑,
- 通常使用簡單的框圖來解釋 App 的架構,比如,Apple 的 MVC 模式可以通過 model、 view 和 controller 三層結構來描述,如下所示:

- 在一個 MVC 專案中,絕大部分的代碼都會集中其中的某個層級上,但這種簡單的框圖幾乎無法解釋在實踐中模式的操作方式,這是因為在實際的 App 架構中, 部件的構建有非常多的可能性,比如說:事件流在層中穿梭的方式是什么?部件之間是否應該在編譯期間或者運行時持有對方?要怎么讀取和修改不同部件中的資料?以及狀態的變更應該以哪條路徑在 App 中進行?
② Model 和 View
- 在最高的層級上,App 架構其實就是一套分類,不同的部件會被歸納到某個型別中去,科技將這些不同的種類叫做層次,一個層次指的是遵循一些基本規則并負責特定 功能的介面和其他代碼的集合,
- Model 層是 App 的內容,它不依賴于 (像 UIKit 那樣的) 任何 App 框架,也就是說,程式員對 model 層有完全的控制,Model 層通常包括 model 物件 (在錄音 App 中的例子是檔案夾和錄音物件) 和協調物件 (比如 App 中的負責在磁盤上存盤資料的 Store 型別),被存盤在磁盤上的那部分 model 稱之為檔案 model (documentation model),
- View 層是依賴于 App 框架的部分,它使 model 層可?,并允許用戶進行互動,從而將 model 層轉變為一個 App,當創建 iOS 應用時,view 層幾乎總是直接使用 UIKit,不過,也會看到在有些架構中,會使用 UIKit 的封裝來實作不同的 view 層,另外,對一些其他的像是游戲類的自定義應用,view 層可以不是 UIKit 或者 AppKit,它可能是 SceneKit 或者 OpenGL 的某種封裝,
- 有時候,選擇使用結構體或者列舉來表示 model 或者 view 的實體,而不使用類的物件, 在實踐中,型別之間的區別非常重要,但是當在 model 層中談到物件、結構體和列舉時, 會將三者統一地稱為 model 物件,類似地,也會把 view 層的實體叫做 view 物件,實際上它們也可能是物件、結構體或者列舉,
- View 物件通常會構成一個單一的 view 層級,在這個層級中,所有的 view 物件通過樹結構的方 式連接起來,在樹的根部是整個螢屏,螢屏中存在若干視窗,接下來在樹的分支和葉子上是更多的小 view,類似地,view controller 也通常會形成 view controller 層級,不過,model 物件卻不需要有明確的層級關系,在程式中它們可以是互不關聯的獨立 model,
- 當提到 view 時,通常指的是像一個按鈕或者一個文本 label 這樣的單一 view 物件,當提到 model 時,通常指的也是像一個 Recording 實體或者 Folder 實體這樣的單個 model 物件,在該話題的大多數文獻中,model 在不同背景關系中指的可能是不同的事情,它可以指代一個 model 層,model 層中的具體的若干物件,檔案 model,或者是 model 層中不關聯的檔案,
- 定義一個 model 層的最重要的理由是,它為程式提供一個表述事實的單一來源,這會讓邏輯清晰,行為正確,我們的程式便不會被應用框架中的實作細節所支配,如果 model 層能做到和應用框架分離,就可以完全在 App 的范圍之外使用它,我們可以很容易地在另外的測驗套件中運行它,或者用一個完全不同的應用框架重寫新的 view 層,那么這個 model 層將能夠用于 Android,macOS 或者 Windows 版本的 App 中,
③ App 的本質是反饋回路
- View 層和 model 層需要交流,因此兩者之間需要存在連接,假設 view 層和 model 層是被清晰地分開,而且不存在無法解耦的聯結的話,兩者之間的通訊需要如下所示:

- 從根本上說,用戶界面是一個同時負責展示和輸入功能的反饋設備,所以毫無疑問,這導致的結果就是一個反饋回路,每個 App 設計模式所面臨的挑戰是如何處理上圖中的箭頭所包含的 交流,依賴和變換,
- 在 model 層和 view 層之間不同的路徑擁有不同的名稱:
-
- 用戶發起的事件會導致 view 的回應, 把由此引起的代碼路徑稱為 view action,像是點擊按鈕或者選中 table view 中的某一行就屬于 view action,
-
- 當一個 view action 被送到 model 層時,它會被轉變為 model action (或者說,讓 model 物件執行一個 action 或者進行更新的命令),這種命令也被叫做一個訊息 (特別 在當 model 是被 reducer 改變時),
-
- 將 view action 轉變為 model action 的操作,以及路徑上的其它邏輯被叫做互動邏輯,
- 一個或者多個 model 物件上狀態的改變叫做 model 變更,Model 的變更通常會觸發一個 model 通知,比如說從 model 層發出一個可觀測的通知,它描述 model 層中什么內容發生了改變,當 view 依賴于 model 資料時,通知會觸發一個 view 變更,來更改 view 層中的內容,這些通知可以以多種形式存在:Foundation 中的 Notification、代理、回呼、或者是其他機制, 都是可以的,將 model 通知和資料轉變為 view 更改的操作,以及路徑上的其他邏輯被叫做表現邏輯,
- 根據 App 模式的不同,有些狀態可能是在檔案 model 之外進行維護的,這樣一來,更新這些狀 態的行為就不會追隨檔案 model 的路徑,在很多模式中的導航狀態就這種行為的一個常?例 子,在 view 層級中的某個部分 (或者按照 Cocoa Storyboard 中使用的術語,將它稱為 scene) 可能會被換出或者換入層級中,
- 在 App 中非檔案 model 的狀態被叫做 view state,在 Cocoa 里,大部分 view 物件都管理著它 們自己的 view state,controller 物件則管理剩余的 view state,在 Cocoa view state 的框圖 中,通常會有加在反饋回路上的捷徑,或者單個層自身進行回圈,在有一些架構中,view state 不屬于 controller 層,而是屬于 model 層的部分 (不過,根據定義,view controller 并不是文 檔 model 的一部分),
- 當所有的狀態都在 model 層中被維護,而且所有的變更都通過完整的反饋回路路徑進行傳遞時,就將它稱為單向資料流,當任意的 view 物件或者中間層物件只能夠通過 model 發出的通知來進行創建和更新 (換句話說,view 或者中間層不能通過捷徑來更新自身或者其他的 view) 時,這個模式通常就是單向的,
④ 架構技術
- Apple 平臺的標準 Cocoa 框架提供了一些架構工具:
-
- Notification 將值從單一源廣播給若干個收聽者;
-
- 鍵值觀察 (KVO) 可以將某個物件上屬性的改變報告給另一個物件,
- 使用到的第三方技術中可能包含了回應式編程,回應式編程也是一種用來交流變更的工具, 不過和通知或者 KVO 不同的是,它專注于在源和目標之間進行變形,讓邏輯可以在部件之間傳輸資訊的同時得以表達,
- 可以使用像是回應式編程或者 KVO 這樣的技術創建屬性系結,系結接受一個源和一個目 標,無論何時,只要源發生了變化,目標也將被更新,這和手動進行觀察在語法上有著不同, 不再需要寫觀察的邏輯,而只需要指定源和目標,接下來框架將會處理其余部分的 作業,
- macOS 上的 Cocoa 包含有 Cocoa 系結技術,它是一種雙向系結,所有的可觀察物件同時也是 觀察者,在一個方向上建立系結連接,會在反方向也創建一個連接,不論是 RxCocoa,還是 CwlViews,都不是雙向系結的,
⑤ App 任務
- 要讓程式正常作業,view 必須依賴于 model 資料來生成和存在,配置 view,讓它可以對model 進行更改,并且能在 model 更新時也得到更新, 所以,需要決定在 app 中如何執行下列任務:
-
- 構建—誰負責構建 model 和 view,以及將兩者連接起來?
-
- 更新 model,如何處理 viewaction?
-
- 改變 view,如何將 model 的資料應用到 view 上去?
-
- viewstate 如何處理導航和其他一些 modelstate 以外的狀態?
-
- 測驗為達到一定程度的測驗覆寫,要采取怎樣的測驗策略?
二、常用的設計模式
① Model-View-Controller
- 標準的 CocoaModel-View-Controller(MVC) 是 Apple 在示例專案中所采用的設計模式,它是 Cocoa App 中最為常?的架構,同時也是在 Cocoa 中討論架構時所采用的基準線,
- 在 Cocoa MVC 中,一部分 controller 物件負責處理 model 或者 view 層范疇之外的所有任務,這意味著,controller 層接收所有的 view action,處理所有的互動邏輯,發送所有的 model action,接收所有的 model 通知,對所有用來展示的資料進行準備,最后再將它們應用到 view 的變更上去,
- 在上文中的 App 反饋回路的圖,會發現在 model 和 view 之間的箭頭上,幾乎每個標簽都是 controller,而且要知道,上圖中的構建和導航任務并沒有標注出來,它們也會由 controller 來處理,
- 下面是 MVC 模式的框圖,它展示了一個 MVC App 的主要通訊路徑:

- 圖中的虛線部分代表運行時的參考,view 層和 model 層都不會直接在代碼中參考 controller, 實線部分代表編譯期間的參考,controller 實體知道自己所連接的 view 和 model 物件的介面,
- 如果在這個圖示外部描上邊界的話,就得到了一個 MVC 版本的 App 反饋回路,注意:在圖表中其他的路徑并不參與整個反饋回路的路徑 (也就是 view 層和 controller 層上那些指向自身的箭頭),
- 構建
-
- App 物件負責創建最頂層的 view controller,這個 view controller 將加載 view,并且知道應 該從 model 中獲取哪些資料,然后把它們顯示出來,
-
- Controller 要么顯式地創建和持有 model 層,要么通過一個延遲創建的 model 單例來獲取 model,
-
- 在多檔案配置中,model 層由更低層 的像是 UIDocument 或 NSDocument 所擁有,那些和 view 相關的單個 model 物件,通常會 被 controller 所參考并快取下來,
- 更改 Model
-
- 在 MVC 中,controller 主要通過 target/action 機制和 (由 storyboard 或者代碼進行設定的) delegate 來接收 view 事件,
-
- Controller 知道自己所連接的 view,但是 view 在編譯期間卻沒有 關于 controller 介面的資訊,
-
- 當一個 view 事件到達時,controller 有能力改變自身的內部狀態, 更改 model,或者直接改變 view 層級,
- 更改 View
-
- 在所理解的 MVC 中,當一個更改 model 的 view action 發生時,controller 不應該直接去 操作 view 層級,正確的做法是,controller 去訂閱 model 通知,并且在當通知到達時再更改 view 層級,
-
- 這樣一來,資料流就可以單向進行:view action 被轉變為 model 變更,然后 model 發送通知,這個通知最后被轉為 view 變更,
- View State
-
- View state 可以按需要被 store 在 view 或者 controller 的屬性中,相對于影響 model 的 view action,那些只影響 view 或 controller 狀態的 action 則不需要通過 model 進行傳遞,
-
- 對于 view state 的存盤,可以結合使用 storyboard 和 UIStateRestoring 來進行實作,storyboard 負責記錄活躍的 controller 層級,而 UIStateRestoring 負責從 controller 和 view 中讀取資料,
- 測驗
-
- 在 MVC 中,view controller 與 app 的其他部件緊密相連,邊界的缺失使得為 controller 撰寫單元測驗和介面測驗十分困難,集成測驗是余下的為數不多的可行測驗手段之一,
-
- 在集成測驗中,構建相連接的 view、model 和 controller 層,然后操作 model 或者 view,來測驗是 否能得到我們想要的結果,集成測驗的書寫非常復雜,而且它涵蓋的范圍太廣了,它不僅僅測驗邏輯,也測驗部件是如何 連接的 (雖然在一些情況下和 UI 測驗的?度并不相同),
-
- 不過,在 MVC 中通過集成測驗,通常達到 80% 左右的測驗覆寫率是有可能的,
- MVC 的重要性:因為 Apple 在所有的實體專案中都使用了這種模式,加上 Cocoa 本身就是針對這種模式設計的,所以 Cocoa MVC 成為了 iOS、macOS、tvOS 和 watchOS 上官方認證的 App 架構模式,
② Model-View-ViewModel + 協調器
- MVVM 和 MVC 類似,也是通過基于場景 (scene,view 層級中可能會在導航發生改變時切入或者換出的子樹) 進行的架構,相較于 MVC,MVVM 在每個場景中使用 view-model 來描述場景中的表現邏輯和互動邏輯,
- View-model 在編譯期間不包含對 view 或者 controller 的參考,它暴露出一系列屬性,用來描述每個 view 在顯示時應有的值,把一系列變換運用到底層的 model 物件后,就能得到這些最終可以直接設定到 view 上的值,
- 實際將這些值設定到 view 上的作業,則由預先建立的系結來完成,系結會保證當這些顯示值發生變化時,把它設定到對應的 view 上去,回應式編程是用來表達這類宣告和變換關系的很好的工具,所以它天生就適合 (雖說不是嚴格必要) 被用來處理 view-model,在很多時候,整個 view-model 都可以用回應式編程系結的方式,以宣告式的形式進行表達,
- 在理論上,因為 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 的參考,
- View-model 可以從 view controller 和 view 中獨立出來,也可以被單獨測驗,同樣,view controller 也不再擁有內部的 view state,這些狀態也被移動到了 view-model 中,在 MVC 中 view controller 的雙重?色 (既作為 view 層級的一部分,又負責協調 view 和 model 之間的互動),減少到了單一?色 (view controller 僅僅只是 view 層級的一部分),
- 協調器模式的加入進一步減少了 view controller 所負責的部分:現在它不需要關心如何展示其它的 view controller 了,因此,這實際上是以添加了一層 controller 介面為代價,降低了 view controller 之間的耦合,
- 構建
-
- 對于 model 的創建和 MVC 中的保持不變,通常它是一個頂層 controller 的職責,不過,單獨的 model 物件現在屬于 view-model,而不屬于 view controller,初始的 view 層級的創建和 MVC 中的一樣,通過 storyboard 或者代碼來完成,
-
- 和 MVC 不同的是,view controller 不再直接為每個 view 獲取和準備資料,它會把這項作業交給 view-model, View controller 在創建的時候會一并創建 view-model,并且將每個 view 系結到 view-model 所暴露出的相應屬性上去,
- 更改 Model
-
- 在 MVVM 中,view controller 接收 view 事件的方式和 MVC 中一樣 (在 view 和 view controller 之間建立連接的方式也相同),
-
- 不過,當一個 view 事件到達時,view controller 不會去改變自身的內部狀態、view state、或者是 model,相對地,它立即呼叫 view-model 上的方 法,再由 view-model 改變內部狀態或者 model,
- 更改 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 變化實際發生之后再通知相關的觀察者,View State
View state 要么存在于 view 自身之中,要么存在于 view-model 里,
- 為了實作單向資料流,view-model 總是應該將變更 model 的 view action 發送給 model,并 且僅僅在 model 變化實際發生之后再通知相關的觀察者,View State
-
- 和 MVC 不同,view controller 中不存在任何 view state,View-model 中的 view state 的變更,會被 controller 觀 察到,不過 controller 無法區分 model 的通知和 view state 變更的通知,當使用協調器時, view controller 層級將由協調器進行管理,
- 測驗
-
- 因為 view-model 和 view 層與 controller 層是解耦合的,所以可以使用介面測驗來測驗 view-model,而不需要像 MVC 里那樣使用集成測驗,介面測驗要比集成測驗簡單得多,因為 不需要為它們建立完整的組件層次結構,
-
- 為了讓介面測驗盡可能覆寫更多的范圍,view controller 應當盡可能簡單,但是那些沒有被移出 view controller 的部分仍然需要單獨進行測驗,在實作中,這部分內容包括與協調器的互動,以及初始時負責創建作業的代碼,
- MVVM 是 iOS 上最流行的 MVC 的非直接變形的 App 設計模式,換言之,它和 MVC 相比,并沒有非常大的不同,兩者都是圍繞 view controller 場景構建的,而且所使用的機制也大都相同,
- 最大的區別可能在于 view-model 中對回應式編程的使用,它被用來描述一系列的轉換和依賴關系,通過使用回應式編程來清晰地描述 model 物件與顯示值之間的關系,為我們從總體上 理解應用中的依賴關系提供了重要的指導,
- iOS 中的協調器是一種很有用的模式,因為管理 view controller 層級是一件非常重要的事情,協調器在本質上并沒有和 MVVM 系結,它也能被使用在 MVC 或者其他模式上,
③ Model-View-Controller + ViewState
- MVC+VS 是為標準的 MVC 帶來單向資料流方式的一種嘗試,在標準的 Cocoa MVC 中,view state 可以由兩到三種不同的路徑進行操作,MVC+VS 則試圖避免這點,讓 view state 的處理更加易于管理,在 MVC+VS 中,明確地在一個新的 model 物件中,對所有的 view state 進行定義和表達,把這個物件叫做 view state model,
- 在 MVC+VS 中,不會忽略任何一次導航變更,串列選擇,文本框編輯,開關變更,model 展示或者滾動位置變更 (或者其他任意的 view state 變化),我們將這些變更發送給 view state model,每個 view controller 負責監聽 view state model,這樣變更的通訊會非常直接,在表現或者互動邏輯部分,不從 view 中去讀取 view state ,而是從 view state model 中去獲取它們:

- 結果所得到的圖表和 MVC 類似,但 controller 的內部反饋回路的部分 (被用來更新 view state) 有所不同,現在它和 model 的回路類似,形成了一個獨立的 view state 回路,
- 構建:和傳統的 MVC 一樣,將檔案 model 資料應用到 view 上的作業依然是 view controller 的責任, view controller 還會使用和訂閱 view state ,因為 view state model 和檔案 model 都需要觀察,所以相比于典型的 MVC 來說,需要多得多的通過通知進行觀察的函式,
- 更改 Model:當 view action 發生時,view controller 去變更檔案 model (這和 MVC 保持不變) 或者變更 model state,不會去直接改變 view 層級,所有的 view 變更都要通過檔案 model 和 view state model 的通知來進行,
- 更改 View:Controller 同時對檔案 model 和 view state model 進行觀察,并且只在變更發生的時候更新 view 層級,
- View State:View State 被明確地從 view controller 中提取出來,處理的方法和 model 是一樣的:controller 觀察 view state model,并且對應地更改 view 層級,
- 測驗:
-
- 在 MVC+VS 中,使用和 MVC 里類似的集成測驗,但是測驗本身會非常不同,所有的測驗都從一個空的根 view controller 開始,然后通過設定檔案 model 和 view state model,這個根 view controller 可以構建出整個 view 層級和 view controller 層級,MVC 的集成測驗中最困 難的部分 (設定所有的部件) 在 MVC+VS 中可以被自動完成,
-
- 要測驗另一個 view state 時,我 們可以重新設定全域 view state,所有的 view controller 都會調整自身,
-
- 一旦 view 層級被構建,可以撰寫兩種測驗:第一種測驗負責檢查 view 層級是不是按照期望被建立起來,第二種測驗檢查 view action 有沒有正確地改變 view state,
- MVC+VS 主要是用來對 view state 進行教學的工具,在一個非標準 MVC 的 App 中,添加一個 view state model,并且在每個 view controller 中 (在已經對 model 進行觀察的基礎上) 觀察這些 view state model,提供了不少優點:任意的狀態恢復 (這種恢復不依賴于 storyboard 或者 UIStateRestoration),完整的用戶界面日志,以及為了除錯目的,在不同的 view state 間進行跳轉的能力,
④ Model 配接器-View 系結器 (MAVB)
- MAVB 是一種以系結為中心的實驗模式,在這個模式中,有三個重要的概念:view 系結器, model 配接器,以及系結,
- View 系結器是 view (或者 view controller) 的封裝類:它構建 view,并且為它暴露出一個系結串列,一些系結為 view 提供資料 (比如,一個標簽的文本),另一些從 view 中發出事件 (比如, 按鈕點擊或者導航變更),
- 雖然 view 系結器可以含有動態系結,但是 view 系結器本身是不可變的,這讓 MAVB 也成為了一種宣告式的模式:宣告 view 系結器和它們的 action,而不是隨著時間去改變 view 系結器,
- Model 配接器是可變狀態的封裝,它是由所謂的 reducer 進行實作的,Model 配接器提供了一個 (用于發送事件的) 輸入系結,以及一個 (用于接收更新的) 輸出系結,
- 在 MAVB 中,不會去直接創建 view,相對地,只會去創建 view 系結器,同樣地,也從 來不會去處理 model 配接器以外的可變狀態,在 view 系結器和 model 配接器之間的 (兩個方 向上的) 變換,是通過 (使用標準的回應式編程技術) 來對系結進行變形而完成的,
- MAVB 移除了對 controller 層的需求,創建邏輯通過 view 系結器來表達,變換邏輯通過系結來表達,而狀態變更則通過 model 配接器來表達,結果得到的框圖如下:

- 構建:
-
- Model 配接器 (用來封裝主 model ) 和 view state 配接器 (封裝頂層的 view state) 通常是在 main.swift 檔案中進行創建的,這早于任何的 view,
-
- View 系結器使用普通的函式進行構建,這些函式接受必要的 model 配接器作為引數,實際的Cocoa view 則由框架負責進行創建,
- 更改 Model:
-
- 當一個 view (或者 view controller) 可以發出 action 時,對應的 view 系結允許指定一個 action 系結,在這里,資料從 view 流向 action 系結的輸出端,
-
- 典型情況下,輸出端會與一個 model 配接器相連接,view 事件會通過系結進行變形,成為 model 配接器可以理解的一條訊息,這條訊息隨后被 model 配接器的 reducer 使用,并改變狀態,
- 更改 View:當 model 配接器的狀態發生改變時,它會通過輸出信號產生通知,在 view 系結器中,可以將 model 配接器的輸出信號進行變形,并將它系結到一個 view 屬性上去,這樣一來,view 屬性就會在一個通知被發送時自動進行變更,
- View State:View state 被認為是 model 層的一部分,View state action 以及 view state 通知和 model action 以及 model 通知享有同樣的路徑,
- 測驗:
-
- 在 MAVB 中,通過測驗 view 系結器來測驗代碼,由于 view 系結器是一組系結的串列,可以驗證系結包含所期望的條目,而且它們的配置正確無誤,可以和使用系結來測驗初始構建以及發生變化時的情況,
-
- 在 MAVB 中進行的測驗,與在 MVVM 中的測驗很相似,不過,在 MVVM 中,view controller 有可能會包含邏輯,這導致在 view-model 和 view 之間有可能會存在沒有測驗到的代碼,而 MAVB 中不存在 view controller,系結代碼是 model 配接器和 view 系結器之間的唯一的代碼, 這樣一來,保證完整的測驗覆寫要簡單得多,
⑤ Elm 架構 (TEA)
- TEA 和 MVC 有著根本上的不同,在 TEA 中,model 和所有的 view state 被集成為一個單個狀 態物件,所有 App 中的變化都通過向狀態物件發送訊息來發生,一個叫做 reducer 的狀態更新 函式負責處理這些訊息,
- 在 TEA 中,每個狀態的改變會生成一個新的虛擬 view 層級,它由輕量級的結構體組成,描述 了 view 層級應該看上去的形式,虛擬 view 層級能夠使用純函式的方式來寫 view 部分的代碼,虛擬 view 層級總是直接從狀態進行計算,中間不會有任何副作用,當狀態發生改變時,使用同樣的函式重新計算 view 層級,而不是直接去改變 view 層級,
- Driver 型別 (這是 TEA 框架中的一部分,它負責持有對 TEA 中其他層的參考) 將對虛擬 view 層級和 UIView 層級進行比較,并且對它進行必要的更改,讓 view 和它們的虛擬版本相符合,這個 TEA 框架中的 driver (驅動) 部件是隨著 App 的啟動而被初始化的,它自身并不知道要對應哪個特定的 App,要在它的初始化方法中傳入這些資訊:包括 App 的初始狀態,一個通過訊息更新狀態的函式,一個根據給定狀態渲染虛擬 view 層級的函式,以及一個根據給定狀態計算通知訂閱的函式 (比如,可以訂閱某個 model store 更改時所發出的通知),
- 從框架的使用者的視?來看,TEA 的關于更改部分的框圖是這樣的:

- 如果追蹤這張圖表的上面兩層,會發現在 view 和 model 之間存在在本文開頭是就說過的反饋回路,這是一個從 view 到狀態,然后再回傳 view 的回路 (通過 TEA 框架進行協調),
- 上面的回路代表的是 TEA 中處理副作用的方式 (比如將資料寫入磁盤中):當在狀態更新方法中處理訊息時,可以回傳一個命令,這些命令會被 driver 所執行,在上面的例子中,最重要的命令是更改 store 中的內容,store 反過來又被 driver 所持有的訂閱者監聽,這些訂閱者可以觸發訊息來改變狀態,狀態最終觸發 view 的重新渲染作為回應,
- 構建:狀態在啟動時被構建,并傳遞給運行時系統 (也就是 driver),運行時系統擁有狀態,store 是一個單例,初始的 view 層級和之后更新時的 view 層級是通過同樣的路徑構建的:通過當前的狀態,計算 出虛擬 view 層級,運行時系統負責更新真實的 view 層級,讓它與虛擬 view 層級相匹配,
- 更改 Model:虛擬 view 擁有與它們所關聯的訊息,這些訊息在一個 view 事件發生時會被發送,Driver 可以接收這些訊息,并使用更新方法來改變狀態,更新方法可以回傳一個命令 (副作用),比如想在 store 中進行的改動,Driver 會截獲該命令并執行它,TEA 讓 view 不可能直接對狀態或者 store 進行更改,
- 更改 View:運行時系統負責這件事,改變 view 的唯一方式是改變狀態,所以,初始化創建 view 層級和更新 view 層級之間沒有區別,
- View State:View state 是包含在整體的狀態之中的,由于 view 是直接從狀態中計算出來的,導航和互動狀態也同樣會被自動更新,
- 測驗:
-
- 在大多數架構中,讓測驗部件彼此相連往往要花費大量努力,在 TEA 中,不需要對此進行 測驗,因為 driver 會自動處理這部分內容,類似地,不需要測驗當狀態變化時 view 會正確 隨之變化,所需要測驗的僅僅是對于給定的狀態,虛擬 view 層級可以被正確計算,
-
- 要測驗狀態的變更,可以創建一個給定的狀態,然后使用 update 方法和對應的訊息來改 變狀態,然后通過對比之前和之后的狀態,就可以驗證 update 是否對給定的狀態和訊息回傳了所期望的結果,
-
- 在 TEA 中,還可以測驗對應給定狀態的訂閱是不是正確,和 view 層級一樣,update 函式和訂閱也都是純函式,
-
- 因為所有的部件 (計算虛擬 view 層級,更新函式和訂閱) 都是純函式,可以對它們進行完全隔離的測驗,任何框架部件的初始化都是不需要的,只用將引數傳遞進去,然后驗證結果就行,
三、其他架構模式
① Model-View-Presenter
- Model-View-Presenter (MVP) 是一種在 Android 上很流行的模式,在 iOS 中,也有相應的實作,在總體結構和使用的技術上,它粗略來說是一種位于標準 MVC 和 MVVM 之間的模式,
- MVP 使用單獨的 presenter 物件,它和 MVVM 中 view-model 所扮演的?色一樣,相對 view-model 而言,presenter 去除了回應式編程的部分,而是把要展示的值暴露為介面上的屬 性,不過,每當這些值需要變更的時候,presenter 會立即將它們推送到下面的 view 中去 (view 將自己作為協議暴露給 presenter),
- 從抽象的觀點來看,MVP 和 MVC 很像,Cocoa 的 MVC,除了名字以外,就是一個 MVP, 它是從上世紀九十年代 Taligent 的原始的 MVP 實作中派生出來的 View,狀態和關聯的邏輯在兩個模式中都是一樣的,不同之處在于,現代的 MVP 中有一個分離的 presenter 物體,它使用協議來在 presenter 和 view controller 之間進行界定,Cocoa 的 MVC 讓 controller 能夠直接參考 view,而 MVP 中的 presenter 只能知道 view 的協議,
- 有些開發者認為協議的分離對于測驗是必要的,當在討論測驗時,會看到標準的 MVC 在沒有任何分離的情況下,也可以被完整測驗,所以感覺 MVP 并沒有太大不同,如果對測驗一個完全解耦的展示層有強烈需求的話,認為 MVVM 的方式更簡單一些,讓 view controller 通過觀察去從 view-model 中拉取值,而不是讓 presenter 將值推送到一個協議中去,
② VIPER,Riblets,和其他 “Clean” 架構
- VIPER,Riblets 和其他類似的模式嘗試將 Robert Martin 的 “Clean Architecture” 從 web app 帶到 iOS 開發中,它們主要把 controller 的職責分散到三到四個不同的類中,并用嚴格的順序將它們排列起來,在序列中的每個類都不允許直接參考序列中前面的類,
- 為了強制單方向的參考這一規則,這些模式需要非常多的協議和類,以及在不同層中傳遞資料的方式,由于這個原因,很多使用這些模式的開發者會去使用代碼生成器,我們的感覺是,這些代碼生成器,以及任何的繁雜到需要生成器的模式,都產生了一些誤導,將 “Clean” 架構帶 到 Cocoa 的嘗試通常都宣稱它們可以管理 view controller 的 “肥大化” 問題,但這么做往往讓代碼庫變得更大,
- 雖然將介面分解是控制代碼尺寸的一種有效手段,但是我們認為這應該按需進行,而不是教條式地對每個 view controller 都這么操作,分解介面需要我們對資料以及所涉及到的任務有清楚 的認識,只有這樣,我們才能達到最優的抽象,并在最大程度上降低代碼的復雜度,
③ 基于組件的架構 (React Native)
- 如果選擇使用 JavaScript 而不是 Swift 編程,或者 App 重度依賴于 web API 的互動, JavaScript 會是更好的選擇,這時可能會考慮 React Native,
- 如果想要找一些類似 React Native,但是是基于 Swift 的東西的話,可以看看對 TEA 的探索,MAVB 的實作也從 ComponentKit 中獲得了一些啟發,而 ComponentKit 本身又從 React 中獲取靈感:它使用類 DSL 的語法來進行宣告式和可變形的 view 構建,這和 React 中 Component 的 render 方法及其相似,
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/290051.html
標籤:其他
