<acronym id="s8ci2"><small id="s8ci2"></small></acronym>
<rt id="s8ci2"></rt><rt id="s8ci2"><optgroup id="s8ci2"></optgroup></rt>
<acronym id="s8ci2"></acronym>
<acronym id="s8ci2"><center id="s8ci2"></center></acronym>
0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

iOS APP架構設計

Android編程精選 ? 來源:稀土掘金技術社區 ? 2023-07-12 09:57 ? 次閱讀

轉自:掘金 iOS一葉

iOS APP 架構設計

一,APP架構概述

應用架構

2.Model 和 View

3 ^[1]^ . App 的本質是反饋回路

4.架構技術

5.App^[2]^ 任務

6.iOS 架構的5中模式:

二,APP設計常用的5種模式概覽

Model-View-Controller

Model-View-ViewModel+協調器

Model-View-Controller+ViewState

Model 適配器-View 綁定器 (MAVB)

Elm 架構 (TEA)

三,其他APP架構模式

Model-View-Presenter

VIPER,Riblets,和其他 “Clean” 架構

基于組件的架構 (React Native)

一,APP架構概述

1. 應用架構

App 架構是軟件設計的一個分支,它關心如何設計一個 app 的結構。具體來說,它關注于兩個 方面:如何將 app 分解為不同的接口和概念層次部件,以及這些部件之間和自身的不同操作中 所使用的控制流和數據流路徑。

我們通常使用簡單的框圖來解釋 app 的架構。比如,Apple 的 MVC 模式可以通過 model、 view 和 controller 三層結構來描述。

wKgZomSuCL2Acnu5AAAoFzSPjOc482.png

上面框圖中的模塊展示了這個模式中不同名字的三個層次。在一個 MVC 項目中,絕大部分的代 碼都會落到其中某個層上。箭頭表示了這些層進行連接的方式。

但是,這種簡單的框圖幾乎無法解釋在實踐中模式的操作方式。這是因為在實際的 app 架構中, 部件的構建有非常多的可能性。事件流在層中穿梭的方式是什么?部件之間是否應該在編譯期 間或者運行時持有對方?要怎么讀取和修改不同部件中的數據?以及狀態的變更應該以哪條路 徑在 app 中穿行?

2.Model 和 View

在最高的層級上,app 架構其實就是一套分類,app 中不同的部件會被歸納到某個類型中去。在本書中,我們將這些不同的種類叫做層次:一個層次指的是,遵循一些基本規則并負責特定 功能的接口和其他代碼的集合。

Model 層和 View 層是這些分類中最為常?的兩個。

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 和 View 的分類會被認為是基礎中的基礎

當然啦,就算不區分 model 層和 view 層,寫出一個 app 也是絕對可能的。比如說,在一個簡 單的對話框中,通常就沒有獨立的 model 數據。在用戶點擊 OK 按鈕的時候,我們可以直接從 用戶界面元素中讀取狀態。不過通常情況下,model 層的缺失,會讓程序的行為缺乏對于清晰 規則的依據,這會使得代碼難以維護。

定義一個 model 層的最重要的理由是,它為我們的程序提供一個表述事實的單一來源,這會讓 邏輯清晰,行為正確。這樣一來,我們的程序便不會被應用框架中的實現細節所支配。

應用框架為我們提供了構建 app 所需要的基礎設施。在本書中,我們使用 Cocoa - 或者更精確 說,根據目標平臺,使用 UIKit,AppKit 或者 WatchKit - 來作為應用框架。

如果 model 層能做到和應用框架分離,我們就可以完全在 app 的范圍之外使用它。我們可以很 容易地在另外的測試套件中運行它,或者用一個完全不同的應用框架重寫新的 view 層。這個 model 層將能夠用于 Android,macOS 或者 Windows 版本的 app 中。

3.App 的本質是反饋回路

View 層和 model 層需要交流。所以,兩者之間需要存在連接。假設 view 層和 model 層是被清
晰地分開,而且不存在無法解耦的聯結的話,兩者之間的通訊就需要一些形式的翻譯:

wKgaomSuCL2AdvEgAABo00RGTnk581.png

從根本上說,用戶界面是一個同時負責展示和輸入功能的反饋設備,所以毫無疑問,這導致的 結果就是一個反饋回路。每個 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 中的 Noti?cation,代理,回調,或者是其他機制, 都是可以的。將 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) 時,這個模式通常就是單向的。

4.架構技術

Apple 平臺的標準 Cocoa 框架提供了一些架構工具。Noti?cation 將值從單一源廣播給若干個 收聽者。鍵值觀察 (KVO) 可以將某個對象上屬性的改變報告給另一個對象。然而,Cocoa 中的 架構工具十分有限,我們將會使用到一些額外的框架。

本書中使用到的第三方技術中包含了響應式編程。響應式編程也是一種用來交流變更的工具, 不過和通知或者 KVO 不同的是,它專注于在源和目標之間進行變形,讓邏輯可以在部件之間傳 輸信息的同時得以表達。

我們可以使用像是響應式編程或者 KVO 這樣的技術創建屬性綁定。綁定接受一個源和一個目 標,無論何時,只要源發生了變化,目標也將被更新。這和手動進行觀察在語法上有著不同, 我們不再需要寫觀察的邏輯,而只需要指定源和目標,接下來框架將會為我們處理其余部分的 工作。

macOS 上的 Cocoa 包含有 Cocoa 綁定技術,它是一種雙向綁定,所有的可觀察對象同時也是 觀察者,在一個方向上建立綁定連接,會在反方向也創建一個連接。不論是 (在MVVM-C 的章

節中用到的) RxCocoa,還是 (MAVB 章節 中用到的) CwlViews,都不是雙向綁定的。所以,在

本書中,所有關于綁定的討論都只涉及到單向綁定。

5.App任務

要讓程序正常工作,view 必須依賴于 model 數據來生成和存在,我們配置 view,讓它可以對

model 進行更改,并且能在 model 更新時也得到更新。所以,我們需要決定在 app 中如何執行下列任務:

1.構建—誰負責構建model和view,以及將兩者連接起來?

2.更新model—如何處理viewaction?

3.改變view—如何將model的數據應用到view上去?

4.viewstate—如何處理導航和其他一些modelstate以外的狀態?

5.測試—為了達到一定程度的測試覆蓋,要采取怎樣的測試策略?

6.對于上面五個問題的回答,是構成 app 設計模式的基礎要件。在本書中,我們會逐一研究這些 設計模式。

6.IOS 架構的5中模式:

IOS 架構的5中模式:

標準的CocoaModel-View-Controller(MVC)是Apple在示例項目中所采用的設計模 式。它是 Cocoa app 中最為常?的架構,同時也是在 Cocoa 中討論架構時所采用的基 準線。

Model-View-ViewModel+協調器(MVVM-C)是MVC的變種,它擁有單獨的 “view-model” (視圖模型) 和一個用來管理 view controller 的協調器。MVVM 使用數據 綁定 (通常會和響應式編程一起使用) 來建立 view-model 層和 view 層之間的連接。

Model-View-Controller+ViewState(MVC+VS)這種模式將所有的viewstate集中到 一個地方,而不是讓它們散落在 view 和 view controller 中。這和 model 層所遵循的規 則相同。

Model適配器-View綁定器(ModelAdapter-ViewBinder,MAVB)是本書的一位作者所 使用的實驗性質的架構。MAVB 專注于構建聲明式的 view,并且拋棄 controller,采用 綁定的方式來在 model 和 view 之間進行通訊。

Elm架構(TEA)與MVC或者MVVM這樣的常?架構完全背道而馳。它使用虛擬view 層級來構建 view,并使用 reducer 來在 model 和 view 之間進行交互。

二,APP設計常用的5種模式概覽

1. Model-View-Controller

在 Cocoa MVC 中,一小部分 controller 對象負責處理 model 或者 view 層范疇之外的所有任 務。

這意味著,controller 層接收所有的 view action,處理所有的交互邏輯,發送所有的 model action,接收所有的 model 通知,對所有用來展示的數據進行準備,最后再將它們應用到 view 的變更上去。如果我們去看一下介紹一章中的 app 反饋回路的圖,會發現在 model 和 view 之

間的箭頭上,幾乎每個標簽都是 controller。而且要知道,在這幅圖中,構建和導航任務并沒有 標注出來,它們也會由 controller 來處理。

下面是 MVC 模式的框圖,它展示了一個 MVC app 的主要通訊路徑:

wKgZomSuCL2AM4NgAABgCh-edKQ933.png

圖中的虛線部分代表運行時的引用,view 層和 model 層都不會直接在代碼中引用 controller。實線部分代表編譯期間的引用,controller 實例知道自己所連接的 view 和 model 對象的接口。

如果我們在這個圖標外部描上邊界的話,就得到了一個 MVC 版本的 app 反饋回路。注意在圖 表中其他的路徑并不參與整個反饋回路的路徑 (也就是 view 層和 controller 層上那些指向自身 的箭頭)。

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 中讀取數據。

5.測試

在 MVC 中,view controller 與 app 的其他部件緊密相連。邊界的缺失使得為 controller 編寫 單元測試和接口測試十分困難,集成測試是余下的為數不多的可行測試手段之一。在集成測試 中,我們構建相連接的 view、model 和 controller 層,然后操作 model 或者 view,來測試是 否能得到我們想要的結果。

集成測試的書寫非常復雜,而且它涵蓋的范圍太廣了。它不僅僅測試邏輯,也測試部件是如何 連接的 (雖然在一些情況下和 UI 測試的?度并不相同)。不過,在 MVC 中通過集成測試,通常 達到 80% 左右的測試覆蓋率是有可能的。

MVC 的重要性

因為 Apple 在所有的實例項目中都使用了這種模式,加上 Cocoa 本身就是針對這種模式設計 的,所以 Cocoa MVC 成為了 iOS,macOS,tvOS 和 watchOS 上官方認證的 app 架構模式。

歷史

MVC 這個名字第一次被提出是在 1979 年,Trygve Reenskaug 用它來描述 Smalltalk-76 上已 經存在的 “template pattern” 應用。在他和 Adele Goldberg 討論了術語方面的問題后,MVC 的名字被確定下來 (之前的名字包括 Model-View-Editor 和 Model-View-Tool-Editor 等)。

在原本的構想中,view 是直接 “附著” 在 model 層上,并觀察所有 model 變化的。Controller 存在的目的僅僅是捕捉用戶事件,并把它們轉發給 model。這兩個特性都是 Smalltalk 運行方 式的產物,它們并不是為了現代的 app 框架所設計的,所以今天這種 MVC 的原始構想已經幾 乎絕跡了。

Cocoa 中的 MVC 實現可以追溯到大約 1997 年的 NeXTSTEP 4 的年代。在那之前,所有現在 controller 所擔當的?色,通常都由一個 (像是 NSWindow 那樣的) 高層 view 類來扮演。之后, 從原始的 Smalltalk 的 MVC 實現中所發展出的理念是分離展示部分,也就是 view 層和 model 層應該被完全隔離開,這帶來了一個強烈的需求,那就是要引入一個支持對象來輔助兩者之間 的通訊。

NeXTSTEP 中 controller 的概念和 Taligent 稍早的 Model-View-Presenter 中的 presenter (展示器) 很相似。不過,在現在 Model-View-Presenter 這個名字通常被用來指代那 些通過協議從 controller 中將 view 抽象出來的類似 MVC 的模式。

2. 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 來決定的。

wKgaomSuCL2ACnznAACrKuIUlt8583.png

如果我們忽略掉協調器,那么這張圖表就很像 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 之間的耦合。

1.構建

對于 model 的創建和 MVC 中的保持不變,通常它是一個頂層 controller 的職責。不過,單獨

的 model 對象現在屬于 view-model,而不屬于 view controller。

初始的 view 層級的創建和 MVC 中的一樣,通過 storyboard 或者代碼來完成。和 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 的部分仍然需要單獨進行測試。在我們的實現中,這部分內容包括與協調器 的交互,以及初始時負責創建工作的代碼。

MVVM 的重要性

MVVM 是 iOS 上最流行的 MVC 的非直接變形的 app 設計模式。換言之,它和 MVC 相比,并沒有非常大的不同;兩者都是圍繞 view controller 場景構建的,而且所使用的機制也大都相同。

最大的區別可能在于 view-model 中對響應式編程的使用了,它被用來描述一系列的轉換和依 賴關系。通過使用響應式編程來清晰地描述 model 對象與顯示值之間的關系,為我們從總體上 理解應用中的依賴關系提供了重要的指導。

iOS 中的協調器是一種很有用的模式,因為管理 view controller 層級是一件非常重要的事情。協調器在本質上并沒有和 MVVM 綁定,它也能被使用在 MVC 或者其他模式上。

歷史

MVVM 由 Ken Cooper 和 Ted Peters 提出,他們當時在微軟工作,負責后來變成 Windows Presentation Foundation (WPF) 的項目,這是微軟.NET^[3]^ 的 app 框架,并于 2005 年正式發布。

WPF 使用一種基于 XML,被稱為 XAML 的描述性語言來描述 view 所綁定的某個 view-model 上的屬性。在 Cocoa 中,沒有 XAML,我們必須使用像是 RxSwift 這樣的框架和一些 (通常存 在于 controller 中的) 代碼來完成 view-model 和 view 的綁定。

MVVM 和我們在 MVC 歷史中提到的 MVP 模式非常類似. 不過,在 Cooper 和 Peters 的論述中, MVVM 中 view 和 view-model 的綁定需要明確的框架支持,但 presenter 是通過傳統的手動 方式來傳遞變化。

iOS 中的協調器則是最近才 (重新) 流行起來的,Soroush Khanlou 在 2015 年時在他的網站上描述了這個想法。協調器基于 app controller 這樣的更古老的模式,而它們在 Cocoa 和其他平臺上已經存在了有數十年之久。

3. 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 中去獲 取它們:

wKgZomSuCL2ABuvyAABMS29Ts9M661.png

結果所得到的圖表和 MVC 類似,但 controller 的內部反饋回路的部分 (被用來更新 view state) 有所不同,現在它和 model 的回路類似,形成了一個獨立的 view state 回路。

1.構建

和傳統的 MVC 一樣,將文檔 model 數據應用到 view 上的工作依然是 view controller 的責任, view controller 還會使用和訂閱 view state 。因為 view state model 和文檔 model 都需要觀 察,所以相比于典型的 MVC 來說,我們需要多得多的通過通知進行觀察的函數。

2.更改 Model

當 view action 發生時,view controller 去變更文檔 model (這和 MVC 保持不變) 或者變更 model state。我們不會去直接改變 view 層級,所有的 view 變更都要通過文檔 model 和 view state model 的通知來進行。

3.更改 View

Controller 同時對文檔 model 和 view state model 進行觀察,并且只在變更發生的時候更新 view 層級。

View State

View State 被明確地從 view controller 中提取出來。處理的方法和 model 是一樣的: controller 觀察 view state model,并且對應地更改 view 層級。

4.測試

在 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 的重要性

MVC+VS 主要是用來對 view state 進行教學的工具。

在一個非標準 MVC 的 app 中,添加一個 view state model,并且在每個 view controller 中 (在已經對 model 進行觀察的基礎上) 觀察這些 view state model,提供了不少優點:任意的狀 態恢復 (這種恢復不依賴于 storyboard 或者 UIStateRestoration),完整的用戶界面日志,以及 為了調試目的,在不同的 view state 間進行跳轉的能力。

歷史

這種特定的體系是 Matt Gallagher 在 2017 年開發的教學工具,它被用來展示單向數據流和用 戶界面的時間旅行等概念。這個模式的目標是,在傳統的 Cocoa MVC app 上通過最小的改動, 實現對 view 的狀態在每個 action 發生時都可以進行快照。

4. 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 適配器來表達。結果得到的框圖如下:

wKgaomSuCL2AACZ1AAB0nCQb1bA920.png

1.構建

Model 適配器 (用來封裝主 model ) 和 view state 適配器 (封裝頂層的 view state) 通常是在

main.swift 文件中進行創建的,這早于任何的 view。

View 綁定器使用普通的函數進行構建,這些函數接受必要的 model 適配器作為參數。實際的

Cocoa view 則由框架負責進行創建。2. 更改 Model

當一個 view (或者 view controller) 可以發出 action 時,對應的 view 綁定允許我們指定一個 action 綁定。在這里,數據從 view 流向 action 綁定的輸出端。典型情況下,輸出端會與一個 model 適配器相連接,view 事件會通過綁定進行變形,成為 model 適配器可以理解的一條消 息。這條消息隨后被 model 適配器的 reducer 使用,并改變狀態。

2.更改 View

當 model 適配器的狀態發生改變時,它會通過輸出信號產生通知。在 view 綁定器中,我們可 以將 model 適配器的輸出信號進行變形,并將它綁定到一個 view 屬性上去。這樣一來,view 屬性就會在一個通知被發送時自動進行變更了。

3.View State

View state 被認為是 model 層的一部分。View state action 以及 view state 通知和 model action 以及 model 通知享有同樣的路徑。

4.測試

在 MAVB 中,我們通過測試 view 綁定器來測試代碼。由于 view 綁定器是一組綁定的列表,我 們可以驗證綁定包含了我們所期望的條目,而且它們的配置正確無誤。我們可以和使用綁定來 測試初始構建以及發生變化時的情況。

在 MAVB 中進行的測試,與在 MVVM 中的測試很相似。不過,在 MVVM 中,view controller 有可能會包含邏輯,這導致在 view-model 和 view 之間有可能會存在沒有測試到的代碼。而 MAVB 中不存在 view controller,綁定代碼是 model 適配器和 view 綁定器之間的唯一的代碼, 這樣一來,保證完整的測試覆蓋要簡單得多。

MAVB 的重要性

在我們所討論的主要模式之中,MAVB 沒有遵循某個直接的先例,它既不是從其他平臺移植過 來的模式,也不是其他模式的變種。它自成一派,用于試驗目的,而且一些奇怪。我們在這兒 介紹它的意義在于,它展示了一些很不一樣的東西。不過,這并不是說這個模式沒有從其他模 式中借鑒經驗教訓:像是綁定、響應式編程、領域專用語言以及 reducer 都是已經被熟知的想 法了。

歷史

MAVB 是 Matt Gallagher 在 Cocoa with Love 網站上首先提出的。這個模式參照了 Cocoa 綁 定、函數式響應動畫、ComponentKit、XAML、Redux 以及成千上萬行的使用 Cocoa view controller 的經驗。

本書中的實現使用了 CwlViews 框架來處理 view 構建、綁定器和適配器的實現等工作。

5. 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 的關于更改部分的框圖是這樣的:

wKgZomSuCL2ABmoUAABQEum1Kg4527.png

如果我們追蹤這張圖表的上面兩層,我們會發現在 view 和 model 之間存在我們在本章開頭是 就說過的反饋回路;這是一個從 view 到狀態,然后再返回 view 的回路 (通過 TEA 框架進行協 調)。

下面的回路代表的是 TEA中處理副作用的方式 (比如將數據寫入磁盤中):當在狀態更新方法中 處理消息時,我們可以返回一個命令,這些命令會被 driver 所執行。在我們的例子中,最重要 的命令是更改 store 中的內容,store 反過來又被 driver 所持有的訂閱者監聽。這些訂閱者可 以觸發消息來改變狀態,狀態最終觸發 view 的重新渲染作為響應。

這些事件回路的結構讓 TEA 成為了遵守單向數據流原則的設計模式的另一個例子。

1.構建

狀態在啟動時被構建,并傳遞給運行時系統 (也就是 driver)。運行時系統擁有狀態,store 是一 個單例。

初始的 view 層級和之后更新時的 view 層級是通過同樣的路徑構建的:通過當前的狀態,計算 出虛擬 view 層級,運行時系統負責更新真實的 view 層級,讓它與虛擬 view 層級相匹配。

2.更改 Model

虛擬 view 擁有與它們所關聯的消息,這些消息在一個 view 事件發生時會被發送。Driver 可以 接收這些消息,并使用更新方法來改變狀態。更新方法可以返回一個命令 (副作用),比如我們

想在 store 中進行的改動。Driver 會截獲該命令并執行它。TEA 讓 view 不可能直接對狀態或者 store 進行更改。

3.更改 View

運行時系統負責這件事。改變 view 的唯一方式是改變狀態。所以,初始化創建 view 層級和更

新 view 層級之間沒有區別。4. View State

View state 是包含在整體的狀態之中的。由于 view 是直接從狀態中計算出來的,導航和交互狀 態也同樣會被自動更新。

4.測試

在大多數架構中,讓測試部件彼此相連往往要花費大量努力。在 TEA 中,我們不需要對此進行 測試,因為 driver 會自動處理這部分內容。類似地,我們不需要測試當狀態變化時 view 會正確 隨之變化。我們所需要測試的僅僅是對于給定的狀態,虛擬 view 層級可以被正確計算。

要測試狀態的變更,我們可以創建一個給定的狀態,然后使用 update 方法和對應的消息來改 變狀態。然后通過對比之前和之后的狀態,我們就可以驗證 update 是否對給定的狀態和消息 返回了所期望的結果。在 TEA 中,我們還可以測試對應給定狀態的訂閱是不是正確。和 view 層級一樣,update 函數和訂閱也都是純函數。

因為所有的部件 (計算虛擬 view 層級,更新函數和訂閱) 都是純函數,我們可以對它們進行完 全隔離的測試。任何框架部件的初始化都是不需要的,我們只用將參數傳遞進去,然后驗證結 果就行了。我們 TEA 實現中的大多數測試都非常直截了當。

Elm 架構的重要性

TEA 最早是在 Elm 這?函數式語言中被實現的。所以 TEA 是一種如何用函數式的方法表達 GUI 編程的嘗試。TEA 同時也是最為古老的單向數據流架構。

歷史

Elm 是 Evan Czaplicki 所設計的函數式編程語言,它最初的目的是為了構建前端 web app。TEA 是歸功于 Elm 社區的一個模式,它的出現是語言約束和目標環境相互作用的自然結果。它 背后的思想影響了很多其他的基于 web 的框架,其中包括 React、Redux 和 Flux 等。在 Swift 中,還沒有 TEA 的權威實現,不過我們可以找到不少研究型的項目。在本書中,我們使用 Swift 按我們自己的理解實現了這個模式。主要的工作由 Chris Eidhof 于 2017 年完成。雖然我 們的這個實現還并不是 “產品級” 的,但是許多想法是可以用在生產代碼中的。

三,其他APP架構模式

1. 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 將值推送到一個協 議中去。

2. VIPER,Riblets,和其他 “Clean” 架構

VIPER,Riblets 和其他類似的模式嘗試將 Robert Martin 的 “Clean Architecture” 從 web app 帶到 iOS 開發中,它們主要把 controller 的職責分散到三到四個不同的類中,并用嚴格的順序 將它們排列起來。在序列中的每個類都不允許直接引用序列中前面的類。

為了強制單方向的引用這一規則,這些模式需要非常多的協議,類,以及在不同層中傳遞數據 的方式。由于這個原因,很多使用這些模式的開發者會去使用代碼生成器。我們的感覺是,這 些代碼生成器,以及任何的繁雜到需要生成器的模式,都產生了一些誤導。將 “Clean” 架構帶 到 Cocoa 的嘗試通常都宣稱它們可以管理 view controller 的 “肥大化” 問題,但是讓人啼笑皆 非的是,這么做往往讓代碼庫變得更大。

雖然將接口分解是控制代碼尺寸的一種有效手段,但是我們認為這應該按需進行,而不是教條 式地對每個 view controller 都這么操作。分解接口需要我們對數據以及所涉及到的任務有清楚 的認識,只有這樣,我們才能達到最優的抽象,并在最大程度上降低代碼的復雜度。

3. 基于組件的架構 (React Native)

如果你選擇使用 JavaScript 而不是 Swift 編程,或者你的 app 重度依賴于 web API 的交互, JavaScript 會是更好的選擇,這時你可能會考慮 React Native。不過,本書是專注于 Swift 和 Cocoa 的,所以我們將探索模式的界限定在了這些領域內。

如果你想要找一些類似 React Native,但是是基于 Swift 的東西的話,可以看看我們對 TEA 的 探索。MAVB 的實現也從 ComponentKit 中獲得了一些啟發,而 ComponentKit 本身又從 React 中獲取靈感:它使用類 DSL 的語法來進行聲明式和可變形的 view 構建,這和 React 中 Component 的 render 方法及其相似。

審核編輯:湯梓紅

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • Apple
    +關注

    關注

    1

    文章

    896

    瀏覽量

    52576
  • APP
    APP
    +關注

    關注

    33

    文章

    1543

    瀏覽量

    71736
  • iOS
    iOS
    +關注

    關注

    8

    文章

    3339

    瀏覽量

    149252
  • MVC
    MVC
    +關注

    關注

    0

    文章

    73

    瀏覽量

    13790
  • 架構設計
    +關注

    關注

    0

    文章

    30

    瀏覽量

    6892

原文標題:iOS APP 架構設計

文章出處:【微信號:AndroidPush,微信公眾號:Android編程精選】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    急聘!!互聯網創業項目征募IOS- app產品經理和開發員

    工具等方面的app開發?,F征募有經驗的IOS-app產品經理1名以及程序員2名。(時間相對自由,尊重大牛們的工作習慣,可以兼職)。產品經理要求:1. 熟悉IOS-app開發流程,善于組織架構
    發表于 02-15 15:13

    軟件架構設計教程

    軟件架構設計教程
    發表于 09-26 15:27

    開源框架教程(二)—— iOS APP開發

    如何使用核心模塊開發設備控制功能(iOS APP開發)導讀:本次教程我們在開源框架代碼基礎上做一個控制gokit紅燈開的App,Wi-Fi設備接入機智云客戶端調試工具包括 iOS De
    發表于 11-28 18:14

    如何使用核心模塊開發設備控制功能(iOS APP開發)

    又稱:如何使用核心模塊開發設備控制功能(iOS APP開發)導讀:本次教程我們在開源框架代碼基礎上做一個控制gokit紅燈開的App,Wi-Fi設備接入機智云客戶端調試工具包括 iOS
    發表于 11-29 18:18

    物聯網APP開源框架教程(二)—— iOS APP開發

    物聯網APP開源框架教程(二)—— iOS APP開發又稱:如何使用核心模塊開發設備控制功能(iOS APP開發)導讀:本次教程我們在開源框
    發表于 12-07 10:54

    【汽車電氣架構設計軟件】

    因工作需要,求整車電氣架構設計軟件——PREEvision(盜版),價格可議,WetChat/***,非誠勿擾
    發表于 04-18 14:20

    STM32軟件架構設計的意義

    STM32軟件架構1、架構設計的意義(1)應用代碼邏輯清晰,且避免代碼冗余;(2)代碼通用性,方便軟件高速、有效的移植;(3)各功能獨立,低耦合高內聚;2、總體架構圖3、結構層說明4、遵循規則5、優劣評估6、STM32實例說明
    發表于 08-04 07:23

    為何要進行嵌入式軟件架構設計?如何設計?

    為何要進行嵌入式軟件架構設計?如何進行嵌入式軟件架構設計?
    發表于 11-01 06:31

    對嵌入式系統中的架構設計的理解

    【閱讀這篇文章,你能了解到什么】1. 從事嵌入式開發12年的我,對架構設計的理解;2. 對嵌入式系統中的架構設計要刻意訓練;3. 嵌入式系統開發過程中的一些小技巧;4. 一個用于智能家居項目
    發表于 11-08 08:23

    iOS搭建清晰可用的APP架構的原則

    最近在搭建新項目的iOS框架,一直在思考如何才能搭建出高可用App框架,能否避免后期因為代碼質量問題的重構。以前接手過許多爛代碼,架構松散,底層混亂,缺少規范,導致團隊開發時代碼風格迥異,清晰的項目
    發表于 09-25 16:45 ?0次下載
    <b class='flag-5'>iOS</b>搭建清晰可用的<b class='flag-5'>APP</b><b class='flag-5'>架構</b>的原則

    iOS應用層架構的定義及CDD詳解

    從2010年開始接觸iOS開發到現在,折騰過不少App架構。從MVC到MVVM,VIPER,MVP,以及最新的ReactiveCocoa都做過實戰嘗試,還有其他變種,諸如猿題庫iOS
    發表于 10-12 16:05 ?0次下載
    <b class='flag-5'>iOS</b>應用層<b class='flag-5'>架構</b>的定義及CDD詳解

    SWE.2的軟件架構設

    過程ID:SWE.2 過程名稱:軟件架構設計 過程目的:軟件架構設計過程目的是建立一個架構設計,識別哪些軟件需求應該分配給軟件的哪些要素,并根據已定義的標準評估軟件架構設計。 ? 過程
    的頭像 發表于 01-11 10:36 ?2467次閱讀

    SYS.3的系統架構設

    系統架構設計 過程ID:SYS.3 過程名稱:系統架構設計 ? 過程目的:系統架構設計過程目的,是建立系統架構設計,并確定將哪些系統需求分配給系統的哪些要素,以及根據已定義的準則評估系
    的頭像 發表于 02-13 16:02 ?2396次閱讀

    架構與微架構設

    下面將從芯片的架構設計、微架構設計、使用設計文檔、設計分區、時鐘域和時鐘組、架構調整與性能改進、處理器微架構設計策略等角度進行說明,并以視頻H.264編碼器設計為例。
    的頭像 發表于 05-08 10:42 ?897次閱讀
    <b class='flag-5'>架構</b>與微<b class='flag-5'>架構設</b>計

    SWE.2軟件架構設

    過程ID : SWE.2 過程名稱 : 軟件架構設計 過程目的 : 軟件架構設計過程目的是建立一個架構設計,識別哪些軟件需求應該分配給軟件的哪些要素,并根據已定義的標準評估軟件架構設
    的頭像 發表于 08-24 09:43 ?527次閱讀
    亚洲欧美日韩精品久久_久久精品AⅤ无码中文_日本中文字幕有码在线播放_亚洲视频高清不卡在线观看
    <acronym id="s8ci2"><small id="s8ci2"></small></acronym>
    <rt id="s8ci2"></rt><rt id="s8ci2"><optgroup id="s8ci2"></optgroup></rt>
    <acronym id="s8ci2"></acronym>
    <acronym id="s8ci2"><center id="s8ci2"></center></acronym>