主頁 > 軟體設計 > iOS之深入決議App的架構設計

iOS之深入決議App的架構設計

2021-07-26 07:25:43 軟體設計

一、概述

① 應用架構
  • 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 里,
    • 和 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

標籤:其他

上一篇:一篇文章帶你入門Docker + Nginx.

下一篇:Nginx保姆級教程(四)-就怕你還學不會-Nginx之負載均衡、限流熔斷、限流熔斷、動靜分離

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 面試突擊第一季,第二季,第三季

    第一季必考 https://www.bilibili.com/video/BV1FE411y79Y?from=search&seid=15921726601957489746 第二季分布式 https://www.bilibili.com/video/BV13f4y127ee/?spm_id_fro ......

    uj5u.com 2020-09-10 05:35:24 more
  • 第三單元作業總結

    1.前言 這應該是本學期最后一次寫作業總結了吧。總體來說,對作業的節奏也差不多掌握了,作業做起來的效率也更高了。雖然和之前的作業一樣,作業中都要用到新的知識,但是相比之前,更加懂得了如何利用工具以及資料。雖然之間卡過殼,但總體而言,這幾次作業還算完成的比較好。 2.作業程序總結 相比前兩個單元,此單 ......

    uj5u.com 2020-09-10 05:35:41 more
  • 北航OO(2020)第四單元博客作業暨課程總結博客

    北航OO(2020)第四單元博客作業暨課程總結博客 本單元作業的架構設計 在本單元中,由于UML圖具有比較清晰的樹形結構,因此我對其中需要進行查詢操作的元素進行了包裝,在樹的父節點中存盤所有孩子的參考。考慮到性能問題,我采用了快取機制,一次查詢后盡可能快取已經遍歷過的資訊,以減少遍歷次數。 本單元我 ......

    uj5u.com 2020-09-10 05:35:48 more
  • BUAA_OO_第四單元

    一、UML決議器設計 ? 先看下題目:第四單元實作一個基于JDK 8帶有效性檢查的UML(Unified Modeling Language)類圖,順序圖,狀態圖分析器 MyUmlInteraction,實際上我們要建立一個有向圖模型,UML中的物件(元素)可能與同級元素連接,也可與低級元素相連形成 ......

    uj5u.com 2020-09-10 05:35:54 more
  • 6.1邏輯運算子

    邏輯運算子 1. && 短路與 運算式1 && 運算式2 01.運算式1為true并且運算式2也為true 整體回傳為true 02.運算式1為false,將不會執行運算式2 整體回傳為false 03.只要有一個運算式為false 整體回傳為false 2. || 短路或 運算式1 || 運算式2 ......

    uj5u.com 2020-09-10 05:35:56 more
  • BUAAOO 第四單元 & 課程總結

    1. 第四單元:StarUml檔案決議 本單元采用了圖模型決議UML。 UML檔案可以抽象為圖、子圖、邊的邏輯結構。 在實作中,圖的節點包括類、介面、屬性,子圖包括狀態圖、順序圖等。 采用了三次遍歷UML元素的方法建圖,第一遍遍歷建點,第二、三次遍歷設定屬性、連邊,實作圖物件的初始化。這里借鑒了一些 ......

    uj5u.com 2020-09-10 05:36:06 more
  • 談談我對C# 多型的理解

    面向物件三要素:封裝、繼承、多型。 封裝和繼承,這兩個比較好理解,但要理解多型的話,可就稍微有點難度了。今天,我們就來講講多型的理解。 我們應該經常會看到面試題目:請談談對多型的理解。 其實呢,多型非常簡單,就一句話:呼叫同一種方法產生了不同的結果。 具體實作方式有三種。 一、多載 多載很簡單。 p ......

    uj5u.com 2020-09-10 05:36:09 more
  • Python 資料驅動工具:DDT

    背景 python 的unittest 沒有自帶資料驅動功能。 所以如果使用unittest,同時又想使用資料驅動,那么就可以使用DDT來完成。 DDT是 “Data-Driven Tests”的縮寫。 資料:http://ddt.readthedocs.io/en/latest/ 使用方法 dd. ......

    uj5u.com 2020-09-10 05:36:13 more
  • Python里面的xlrd模塊詳解

    那我就一下面積個問題對xlrd模塊進行學習一下: 1.什么是xlrd模塊? 2.為什么使用xlrd模塊? 3.怎樣使用xlrd模塊? 1.什么是xlrd模塊? ?python操作excel主要用到xlrd和xlwt這兩個庫,即xlrd是讀excel,xlwt是寫excel的庫。 今天就先來說一下xl ......

    uj5u.com 2020-09-10 05:36:28 more
  • 當我們創建HashMap時,底層到底做了什么?

    jdk1.7中的底層實作程序(底層基于陣列+鏈表) 在我們new HashMap()時,底層創建了默認長度為16的一維陣列Entry[ ] table。當我們呼叫map.put(key1,value1)方法向HashMap里添加資料的時候: 首先,呼叫key1所在類的hashCode()計算key1 ......

    uj5u.com 2020-09-10 05:36:38 more
最新发布
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:20:47 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:20:25 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:20:17 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:20:10 more
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:19:44 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:19:07 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:18:57 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:18:49 more
  • 05單件模式

    #經典的單件模式 public class Singleton { private static Singleton uniqueInstance; //一個靜態變數持有Singleton類的唯一實體。 // 其他有用的實體變數寫在這里 //構造器宣告為私有,只有Singleton可以實體化這個類! ......

    uj5u.com 2023-04-19 08:42:51 more
  • 【架構與設計】常見微服務分層架構的區別和落地實踐

    軟體工程的方方面面都遵循一個最基本的道理:沒有銀彈,架構分層模型更是如此,每一種都有各自優缺點,所以請根據不同的業務場景,并遵循簡單、可演進這兩個重要的架構原則選擇合適的架構分層模型即可。 ......

    uj5u.com 2023-04-19 08:42:41 more