iOS 架構

SLPA發表於2020-12-12

初見

MVC

儘管開發人員爭論應該使用哪種體系結構,但 Apple 已經向我們提供了有關如何構建 iOS 應用程式的說明,即 MVC。

iOS 架構

View 是使用者可以在螢幕上看到的部分。Model 是“資料”。Controller 是它們之間的中介。它從 Model 獲取資料並在 View 上顯示給使用者,同時在 View 上處理使用者操作並將其傳輸到 Model。

看起來很好。如果遵循要 Apple 指南的話,為什麼不使用 MVC 呢?因為乍一看,MVC 真的很糟糕。您可能知道,ViewController 的大小和維護難度。因為除了檢視和資料外,還有很多不同的邏輯,這顯然應該由 Controller 完成。

Controller 負責管理其擁有的檢視的檢視層次結構。他們響應檢視的載入,出現,消失等等操作。他們還傾向於處理我們想脫離模型的模型邏輯以及我們想脫離檢視的業務邏輯。這導致我們遇到的第一個問題是 Massive View Controller。

MVVM

我們並不喜歡這樣上面這種方式,因此開始尋找 MVC 替代方案。並且我們找到了它們。

iOS 架構

MVVM 新增了一個新層 ViewModel 來將程式碼與 Controller 分開。但是實際上,它並不能解決所有問題。ViewModel 應該真正包含什麼?當ViewModel 也變得像 Controller 一樣臃腫時,我該怎麼辦?社群也因此分裂為喜歡 MVVM 的人和不喜歡 MVVM 的人。

MVP

解決此問題的另一種嘗試是 MVP。它開始將 ViewController 視為 View,所有邏輯都交給新類 Presenter。但是它並沒有流行起來,因為它看起來真的很奇怪。實際上,我們只是將所有問題從 ViewController 轉移到 Presenter。

iOS 架構

VIPER

然後,我們認為我們需要進一步分解並建立了 VIPER。現在,所有程式碼都進入檢視,演示者,路由器,互動器或實體之一。

iOS 架構

在很短的時間內,VIPER 變得流行起來,但是後來我們知道它有問題。這種體系結構需要大量協議,類以及層之間的資料傳遞。但是由於某些原因,所有這些額外的工作並不能使我們的設計更好,更易讀。

其他架構

最後,我們無休止的去建立新架構。所有這些看起來都是個笑話。每個新架構看起來都比以前的架構更奇怪。吉爾赫姆·蘭博(Guilherme Rambo)講過一個笑話,很好地描述了這種情況的荒謬性。無論選擇哪種架構,所有架構都是不好的。

但是正如我之前所說,這個問題有解決方案。我會告訴你我們應該使用哪種“模式”。您可能會感到驚訝,但實際上就是 MVC。我想要做的是從頭開始,從原始資料中閱讀 MVC,然後停止使用它。如果它還活著,也許還不算壞?

原始的 MVC

許多 iOS 開發人員抱怨 MVC。但是,如果我告訴您,我前面提到的所有 MVC 問題實際上都不存在的呢?諸如“Massive View Controller”,“模型就是資料”,“ ViewController做很多業務邏輯”等觀察都是虛構的。他們都是出於對真正的 MVC 的誤解而產生的。

人們對此有疑問的主要原因是由於 MVC 的過於簡化。說真的,當您聽到 MVC 時,您會怎麼想?“一共有 3 個類:Model 是資料,View 是檢視,Controller 在它們之間”。但是,MVC 並不是那麼簡單。

MVC是一項非常艱鉅的工作的結果。它是由 Trygve Reenskaug 於 1979 年在施樂 PARC 的 Dynabook 專案上的提出的。Dynabook 是適用於所有年齡段兒童的個人計算機。這是一個真正的革命性專案。Dynabook 旨在使計算機易於使用,同時使使用者能夠管理複雜的應用程式。那時,圖形介面的基礎和“使用者友好介面”的概念首先得到了發展。

這個專案進行了大約十年。Reenskaug 總結了這十年在 MVC 中積累的 GUI 應用程式開發的主要思想和解決方案。

並沒有像“嘿,我們在10年內建立了一種通用模式,您應該用它來解決任何問題”。這是我們犯的根本錯誤。MVC 不是模式。這不是應用程式模組分解的方案。沒有人可以為您提供具有一定數量的類的靈丹妙藥解決方案,因為沒人知道您的問題,應用程式的業務邏輯,域模型詳細資訊和主要目標。您應該自己設計應用程式。以下是 Martin Fowler 描述 MVC 誤解的問題:

它通常被稱為模式,但是我認為將其視為一種模式並不是非常有用,因為它包含了許多不同的想法。在不同地方閱讀 MVC 的人不同,他們的想法也不同,並將其描述為 “MVC”。如果這不會引起足夠的混亂,那麼您會得到對 MVC 的誤解,這種誤解是透過層層傳遞而來的。

MVC 是一組架構思想和原則。MVC 是正式嘗試將具有圖形使用者介面的應用程式中的主要思想形式化的嘗試之一。這些想法仍然有意義,不僅適用於 iOS 平臺。您可以從 Trygve Reenskaug 的作品中瞭解有關 MVC 的資訊。 The original MVC reports1 與  The Model-View-Controller. It's Past and Present2。

MVC 的主要原則之一是將我們所有的程式碼劃分為 Presentation 和 Domain Model。

Domain Model 是我們應用程式的核心。這是它的主要部分。它由幾個業務物件組成,例如,諸如帳戶,產品,交易等實體。這些物件相關的邏輯稱為業務邏輯。例如,“如果使用者帳戶上的錢很少,請給他折扣”。MVC 中的模型意味著整個 Domain Model,而不僅僅是某個實體的一個啞模型(dumb model)。Domain Model 可以包含一個物件,也可以包含整個物件系統。這取決於業務邏輯的複雜程度。

Presentation 是使用者可以看到並與之互動的內容。在 MVC 中,View 和 Controller 是 Presentation 的一部分。

馬丁·福勒(Martin Fowler)將此原則稱為 “Separated Presentation”3。

MVC 的核心,也是對後來的框架最有影響力的想法,就是我所說的“分離表示”。分離演示的背後思想是在建模我們對現實世界的感知的領域物件和作為螢幕上看到的 GUI 元素的演示物件之間進行清晰的劃分。領域物件應該完全獨立並且可以在不引用 presentation 的情況下工作,它們還應該能夠支援多個 presentation(可能同時支援)。這種方法也是 Unix 文化的重要組成部分,並且一直持續到今天,允許透過圖形介面和命令列介面來操縱許多應用程式。

因此,如果我們的 Presentation 與 Domain Model 鬆散耦合的,並且無需任何 Domain Model 的詳細資訊,同時 Domain Model 完全獨立於Presentation,則我們應用的設計將是清晰,可重用和可維護的。

該方案取自 Reenskaug 的報告。

iOS 架構

其中的 Editor 是 Presentation 的最初表述。在此方案中,我們可以看到 MVC 不是 3 個部分。它更多地是關於按層而不是按類進行分解。重要的是,Presentation 應與 Domain Model 非常鬆散地耦合。理想情況下,它應該僅取決於所需的介面,以便任何 Domain Model 都可以實現此介面。該方案的 Facade 模式表明,Domain Model 中有一個類可以透過呼叫所需物件來實現此介面,因此 Presentation 不需要了解有關域模型中具體物件的任何知識。介面和外觀幫助我們使 Presentation 和 Domain Model 之間的連線鬆散耦合。

但是 Domain Model 應該如何與 Presentation 通訊?例如,如果某些資料在“Domain Model”中發生了更改,則應如何通知 Presentation?這是 MVC 的另一個原理。Domain Model 永遠不應該依賴於Presentation,即使是透過介面也是如此。Domain Model 所能做的就是傳送有關某個事件的通知,而不知道誰將處理此事件。可以透過觀察者模式來完成。這將使我們完全獨立於域模型。

Reenskaug 報告的另一種方案描述了 MVC 的第三項原則。

iOS 架構

這是關於 Input 和 Output 的分離表示。最初,將 Presentation 分為負責向使用者顯示資訊的層和負責從使用者獲取資訊的層是一個很好的主意。稍後您將看到,該原理不適用於 iOS。但是您應該知道,在原始 MVC 中, Controller 和 View 都具有圖形表示。

總而言之,原始 MVC 應該看起來像這樣:

iOS 架構

這適用於iOS嗎?

當然可以!如果我們將 MVC 視為一組原則,而不僅僅是一個“具有 3 種類的模式”,我們將永遠不會知道 “Massive View Controller” 問題。讓我們看看這些原理如何適用於iOS。

如前所述, MVC 的核心是 Presentation 和 Domain Model 之間的強分離。實際上,該原理已成為 GUI 應用程式設計中的主要原理之一。諸如 MVVM 或 MVP 之類的其他體系結構也基於這種分離。無論您針對哪個平臺編寫程式碼,使用哪種體系結構,都應始終進行這種分離。因此,這意味著該原則對 iOS 也很重要。

如何將檢視劃分為 View 和 Controller?通常,它也適用於 iOS,甚至包含 UIView 和 UIViewController 的 iOS SDK。但是我們應該知道,這種分離與原始 MVC 有一些區別。這並不奇怪,因為經過這麼長的時間,使用者介面也發生了變化。現在,我們不需要在輸入和輸出上劃分圖形元素。特別是在 iOS 上,每個 UIView 元素都能夠顯示資訊並接收使用者操作。因此,UIView 是一個類,具有圖形表示形式,並負責與使用者雙向交流。UIViewController 是 UIView 的所有者。它“控制” View 及其生命週期,在 View 上處理使用者操作,並在 View 上顯示 Model 中的資訊。


iOS 架構

原始 MVC 的這種變體具有不同的名稱,稍後我們將看到它,但是無論如何,我們將其稱為 MVC,因為保留了主要原理,並且僅僅是 MVC 的變體。此外,蘋果公司本身稱之為 MVC。

實際上,我們如何稱呼它並不重要。重要的是要了解它是如何實現的。更確切地說,要意識到已經實現了 MVC。UIView 和 UIViewController 是已經在 iOS SDK 中實現的類。我的意思是,有些人拒絕 MVC,但仍使用 UIView 和 UIViewController。儘管這是主要問題,但它使 Apple MVC 與其他體系結構有所不同。這是我們如何處理使用者互動的一種方式,而諸如 Interactor 或 Presenter 之類的其他類則不會更改這種方式。相反,MVC 在必要時根據問題涉及其他實體。儘管 Interactor 和 Presenter 都是不好的類的示例,但我們應該記住 MVC 並不是一種模式,可以根據需要提供許多類來解決問題。因此,如果您在使用者面前使用 UIView 和 UIViewController,則不必介意建立其他哪些類,您可以使用 Apple MVC。

我們能不使用 UIView 和 UIViewController 嗎?可以!許多工作在後臺進行,因此我們可以輕鬆地透過我們的應用程式處理使用者的所有通訊。除了這兩個類之外,還有很多其他東西:響應者鏈,UIEvent,UIView 層次結構,UIView 生命週期,Hit Testing,UIControls,UIGestureRecognizers 等。所有這些都是 Apple MVC。這意味著 MVC 不是我們的選擇。如果您說自己不使用 MVC,然而事實並非如此!我們使用了 MVC,並且在 iOS 中不能使用任何替代方法。

使用 iOS SDK 進行戰鬥是不可能的,任何嘗試都會使系統複雜化。但是,如果我們瞭解這一點,就還不錯。一旦我們停止與 iOS SDK 的對抗,所有這些東西就會變得有用。SDK 開始幫助我們並從中受益。每個 UIViewController 都擁有一個根 UIView。我們可以在 interface builder 中繪製檢視而無需任何程式碼,並將所有使用者操作連結到UIViewController。UIViewController 還透過諸如viewDidLoad(),viewWillAppear() 等方法處理 View 的狀態。我們應該使用所有這些功能。

iOS SDK 為我們提供了許多功能。許多開發人員抱怨 UIViewControllers 變胖了,但其中只有一小部分提到了 UIViewControllers 分解功能。因此,對於許多開發人員而言,它可能會讓人感到驚訝。但是我們可以為 1 個頁面建立多個 UIViewControllers。是的,如果一個螢幕上有多個邏輯上獨立的元件,我們可以將其分為多個小 UIViewControllers。

• UIViewController 是表示層的一部分。如果您在此處編寫業務邏輯,網路請求或其他與使用者介面無關的內容,則不是 MVC。

• 如果需要,在表示層中建立其他類。IViewController 的存在並不會迫使您在此處編寫所有程式碼。如果您有很多表示邏輯,請從 ViewController 中刪除它。但是請確保確實需要新實體。

• 不要與 iOS SDK 抗爭。它為我們提供了許多功能,如果我們開始使用它們,這些功能將帶來巨大的好處。

我們需要MVC替代品嗎?

好吧,答案很明顯:我們不需要。您已經瞭解了什麼是真正的 MVC,以及如何在 iOS 中使用它。此外,使用自己的體系結構與 iOS 平臺抗衡幾乎是不可能的。但是,讓我們再次考慮一下我們在開始時描述的每種架構,您會發現它們在 iOS 環境中是多麼的奇怪甚至荒謬。

MVP

MVP 是其中最奇怪的一個。MVP 由 Mike Potel 於 1996 年推出,是對 MVC 的修改。在有關 MVP 的工作中,Potel 建議無需將小部件劃分為“檢視”和“控制器”。現代作業系統的使用者介面已經在 View 類中提供了大多數 Controller 功能,因此 Controller 似乎有點多餘。因此,刪除了 Controller 並建立了一個新類 Presenter 作為 View 和 Model 之間的粘合劑。

等等,看起來像 Apple MVC 嗎?也許它就是 Apple MVC?蘋果原本想說是 MVP,卻說成了 MVC?我不知道,因為這些術語之間有太多混淆。讓我們看看 Martin Fowler 在有關 GUI 體系結構的文章中如何區分 MVC 和 MVP。

MVP使用 Supervising Controller 來操縱模型。小部件將使用者手勢傳遞給 Supervising Controller。小部件未分為檢視和控制器。您可以將 presenters 看作是控制器,但無需最初處理使用者手勢。但是,還需要注意的是,presenters 通常是在表單級別,而不是在小部件級別 -– 這也許是更大的區別。

iOS 架構

現在,看看 MVC 和 MVP 的這些方案。並將它們與我們上面看到的 Apple MVC 方案進行比較。其中哪一個與 Apple MVC 更相似?是的,Apple MVC 看起來更像是 MVP,而不是原始的 MVC。我們如何稱呼它並不重要。Apple MVC 無論如何都與它們兩者不同。

作為一個開發者,有一個學習的氛圍跟一個交流圈子特別重要,這有個iOS交流群: 642363427,不管你是小白還是大牛歡迎入駐 ,分享BAT,阿里面試題、面試經驗,討論技術,iOS開發者一起交流學習成長!

最重要的是要了解我們已經擁有充當 UIView 持有者的 UIViewController。這意味著我們不需要具有 Presenter 或 Controller 角色的其他任何類。

因此,嘗試建立一個新的 Presenter 類並將 UIViewController 視為一個檢視是沒有意義的。儘管我說過,除了 UIView 和 UIViewController 之外,Presentation 層中可能還有其他類,但是 Presenter 是這樣做的一個不好的例子。這是與 iOS SDK 對抗的一個示例。無論我們是否希望將 UIViewController 視為 View 多少,它仍然是Controller(或者您可以將其稱為 Presenter)。在 iOS 中,MVP 方案實際上如下所示:

iOS 架構

我們真的需要這個新類嗎?這看起來很奇怪,因為我們只是建立了具有完全相同角色的 UIViewController 的副本。如果沒有給我們帶來任何收益,我們為什麼應該轉移所有使用者操作,將所有檢視狀態從 Controller 更改為 Presenter?它只會給我們帶來額外的程式碼和複雜性。確實很難將每個動作委派給 Presenter。同樣,不要與 iOS SDK 對抗,我們無法將 UIViewController 轉換為 View。即使可以,也沒有必要。

VIPER

還記得我說過 MVP 是最奇怪的嗎?不,VIPER 才是。因為,除了 MVP 的所有問題(它還會重複 Presentation 層中 MVP 的所有錯誤,包括複製 Presenter 以及將 UIViewController 轉換為 View 的嘗試失敗),VIPER 還試圖將我們的 Domain Model 劃分為 Interactor,Service 和 Entity 類。

VIPER 是如何被建立的?是否有自己的歷史記錄,例如 MVC 或 MVP?是的,的確如此,但是歷史並不那麼光鮮。VIPER 於 2013 年建立,旨在解決 Apple MVC 問題。

由於許多應用程式邏輯不屬於模型或檢視,因此通常會在控制器中處理。這導致了一個稱為 Massive View Controller 的問題,在該問題中,檢視控制器最終會做太多事情。

以上引用來自有關 VIPER 的原始帖子。這意味著 VIPER 的建立是為了解決不存在的 “Massive View Controller” 問題。它是基於 “MVC是具有3種類和巨大的UIViewController的模式”的錯誤思想而建立的。為了解決這個“問題”,VIPER 按 5 類進行了更多分解。但是實際上,您的“架構”有多少個字母並不重要。如果您僅將應用程式體系結構視為具有確切類的“模式”,則無論如何都會失敗。類的數量是有限的,但是每次的邏輯可能會更寬,並且有朝一日我們的 Interactor 或另一個類將與 Massive View Controller 一樣大。

而且,邏輯可能真的不同。但是在 VIPER 中,即使邏輯很小或非常具體,我們也總是建立 5 個類。問題確實有所不同,並且沒有適合所有問題的方案。我們應該根據此特定邏輯單獨進行分解。在 OOP 中,常見的任務是瞭解我們應該建立哪些實體,如何將它們彼此關聯以及如何命名它們,從而以最清楚地描述程式碼。

克里斯·艾德霍夫(Chris Eidhof)在《App Architecture》一書中對 VIPER 問題和分解有很多想法。

雖然介面分解是一種管理程式碼大小的有效方法,但我們認為應該按需執行,而不是有條不紊地針對每個檢視控制器執行。分解應該與所涉及的資料和任務的知識一起執行,以便可以實現最佳的抽象,從而可以最大程度地降低複雜性。

Interactor 是否有這麼好的抽象性?答案是否定的。“Interactor 是包含業務邏輯的類”。這有助於我們理解程式碼嗎?它包含哪些業務邏輯?如果我有很多業務邏輯怎麼辦?我們應該建立並命名我們的實體,使其清晰明確,而不僅僅是通用的“Interactor”。

為所有問題建立相同的類,並且每次僅將程式碼新增到這些類中並不是一個好的設計。它甚至都不是 OOP,我認為這是具有 5 個檔案的過程程式設計。

我認為,VIPER 是一個很大的錯誤。VIPER 證明我們還不瞭解 MVC。我的建議是忘記 VIPER,不要討論它。

MVVM

如果我們不使用 UIViewController 編寫業務邏輯並使用分解將一個螢幕劃分為多個 UIViewControllers,那麼我們的 UIViewControllers 永遠不會變得很大嗎?好吧,這取決於它具有多少表示邏輯。

我們來看一個例子。

// Domain Model Objectstruct Person {
    let name: String
    let gender: Gender}class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        // presentation logic
        if person.gender == .male {
            nameLabel.text = "Mr." + person.name        } else {
            nameLabel.text = "Mrs. " + person.name        }
    }}

在上面的示例中,我們有一個表示邏輯,可以根據人的性別來格式化其姓名標題。這個邏輯應該在 UIViewController 中嗎?如果存在很多複雜的表示邏輯怎麼辦?除了複雜性之外,還存在測試問題。測試 UIViewController 類並不容易。這也是開發人員建立自己的 Presenter 並將所有邏輯移至這個 NSObject 子類的另一個原因。但是我們已經看到了這種方法的問題。

我們可以在 Person 類中編寫此邏輯嗎?好了,在這種情況下,我們將根據 MVC 原理將表示和業務邏輯混合在一個不好的類中。很難理解為什麼有此程式碼。我們看不到該程式碼是針對哪個具體檢視編寫的。最後,很難在不同的螢幕上重用此模型。如果在其他頁面上以不同方式顯示此資訊(例如表情符號)怎麼辦?

現在,該再次重申 MVC 不是模式。是的,我們在 Presentation 層中有一些邏輯,MVC 不會強迫您在現有的類中編寫此邏輯。我們可以建立一個新類並在那裡封裝具體邏輯。馬丁·福勒(Martin Fowler)寫了這個問題。他說,如果與 Domain Model 物件不同,我們可以在 Presentation 層中建立其他模型。他稱其為“物件表示模型(Presentation Model)”。

struct PersonPresentation {
    let name: String    init(person: Person) {
        if person.gender == .male {
            name = “Mr.” + person.name        } else {
            name = “Mrs. “ + person.name        }
    }}

現在,在 UIViewController 中,我們僅將名稱對映到標籤。 您可能會說它是 MVVM。但事實並非如此。儘管我們將其稱為 MVVM,但事實並非如此。因為實際上,MVVM 於 2005 年建立,是對 MVC 和 MVP 的修改。新增了 ViewModel 而不是 Controller 和 Presenter,它可以處理 View 操作。但是在 iOS 中,我們仍然沒有擺脫 Controller。UIViewController 處理我們與使用者互動的方式。我們要做的就是在 Presentation 層中建立一個額外的模型,這在 MVC 中是隱含的。我們只是使用了一個 Presentation Model。

但是同樣,命名並不是一個大問題。當然,我們不會將所有 ViewModels 重新命名為 PresentationModels。但是,我們應該瞭解 ViewModel 的真正含義(為避免混淆,現在將其稱為 PresentationModel)。

PresentationModel 不是包含所有業務邏輯的類,很多開發人員都這麼說。PresentationModel 不是將網路請求,資料庫請求,快取等組合在一起的外觀。它只是 Presentation 層中的模型。使用 PresentationModel 並不意味著我們使用另一種架構。我們仍然使用 MVC,因為我們不會更改與使用者互動的方式。

通常,PresentationModel 只是一種模式。是的,與 MVC 或原始 MVVM 不同,Presentation Model 是一種在確實需要時使用的模式。無需進行標準化,也無需無故在每個模組上建立 PresentationModel。

結論

MVC 不像具有 3 種類的方案那麼簡單。MVC 不是模式,而是一組架構思想和原則。

這些原則的核心是 Presentation 和 Domain Model 之間的強分離。MVC 中的模型表示整個域模型。UIViewController 是 Presentation 的一部分。這意味著 MVC 不允許我們建立一個啞實體並將所有業務邏輯移至 UIViewController。

這種分離已成為 GUI 應用程式設計中的主要分離之一,它們對 iOS 也很有用。但是表示層分離通常是特定於平臺的。iOS SDK 已經完成了大量工作,因此我們可以輕鬆地透過我們的應用程式處理使用者的所有交流。因此,MVC 不是我們的選擇,我們無法更改與使用者互動的工作方式。我們不應該與平臺對抗,因為我們的設計會很複雜。但是,一旦我們停止與 iOS SDK 對抗,所有這些人員就會變得有用。

除了根據業務邏輯設計域模型外,我們還可以根據表示邏輯設計表示。MVC 不會強迫我們在 UIViewController 中編寫所有程式碼。如果需要,我們可以在 Presentation 層中建立其他類。但是,如果您新增 ViewModel 或 Coordinator 或其他名稱,則不要將其稱為新架構。

請勿嘗試將架構標準化為模式。根據特定的邏輯分別進行分解,以試圖清楚地描述程式碼。

不要責怪 MVC。

推薦

iOS架構模式影片學習


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69990324/viewspace-2741693/,如需轉載,請註明出處,否則將追究法律責任。

相關文章