如何通過學(xué)習(xí)舊的IOS架構(gòu)來構(gòu)建良好的IOS架構(gòu)?

一些歷史
1979年,Trygve Reenskaug提出了MVC?——用戶控制龐大而復(fù)雜的數(shù)據(jù)集問題的一般解決方案。最初的論文引起了人們的極大興趣,最終,許多公司和個人提出了自己對MVC思想的理解和實現(xiàn),他們并不局限于Model、View和Controller的原始定義。
最初的MVC
1979年的論文中對MVC的描述:
Models
Model代表認(rèn)知,Model可以是單個對象(單一的),也可以是對象的某種結(jié)構(gòu)。
“Models represent knowledge. A model could be a single object (rather uninteresting), or it could be some structure of objects.”
Views
View是其Model的(視覺)表示。它通常會突出Model的某些屬性,并隱藏其他屬性。
“A view is a (visual) representation of its model. It would ordinarily highlight certain attributes of the model and suppress others.”
Controllers
Controller是用戶和系統(tǒng)之間的鏈接。它為用戶提供輸入,安排相關(guān)View在屏幕上的適當(dāng)位置顯示。
“A controller is a link between a user and the system. It provides the user with input by arranging for relevant views to present themselves in appropriate places on the screen.”
PS:Controllers最初的命名是Editors,后來更改了名稱。
到目前為止聽起來很熟悉…

MVC中令人驚訝的事實:
Controllers
Controller接收用戶的輸出,將其轉(zhuǎn)換為適當(dāng)?shù)南?,并將這些消息傳遞給一個或多個view。
“…The controller receives the user’s output, translates it into the appropriate messages and passes these messages on to one or more of the views.”
因此Controller知道并更新View。

Controller永遠不應(yīng)該補充Views,例如,不應(yīng)該將它的View的節(jié)點用(繪制到屏幕上的)箭頭連接起來。
“A controller should never supplement the views, it should for example never connect the views of nodes by drawing arrows between them.”
所以Controller應(yīng)該與視覺表現(xiàn)無關(guān)。
Controller連接到它的所有View,它們(View)作為Controller的部件。
“A controller is connected to all its views, they are called the parts of the controller.”
這意味著只要Controller創(chuàng)建了View,并且不是被注入的,那么它就可能知道Models。
...這個Editor非常類似于之前的Editor,但是它是在network中創(chuàng)建的。因此,該network及其所有activity的消息可以通過方法直接鍵入和執(zhí)行。
“…This Editor is very similar to the previous one, but it has been created in the environment of a demonstration network. Messages to that network and all its activities may therefore be typed in and executed directly through the “doit” command.”
如果我們知道起Editor是Controller的舊名稱,并考慮到“網(wǎng)絡(luò)”和“活動”是Models,我們可以得出這樣的結(jié)論:Controller確實知道并更新Model。

Views
View被附加到它的Model(或Model部分),并從Model中獲取顯示所需的數(shù)據(jù)。它還可以通過發(fā)送適當(dāng)?shù)南砀翸odel。
“…A view is attached to its model (or model part) and gets the data necessary for the presentation of the model by asking questions. It may also update the model by sending appropriate Messages.”
所以Views知道并更新Models.

Surprise!

讓我們把拼圖的所有部分拼在一起:

看起怎么樣?

最初的MVC有兩個重大的問題:
- 它破壞了一些好的軟件設(shè)計規(guī)范。
- 在iOS中實現(xiàn)是不切實際的。
對MVC的改造
單一職責(zé)
Views和Controllers都負(fù)責(zé)更新模型,但我們希望只有一個實體有這樣的職責(zé)。這就是為什么大家提出單向數(shù)據(jù)流:

松耦合與高內(nèi)聚
Views知道Models。Controllers知道Models和Views。這樣的話,Models就會依賴于Views和Controllers。這意味著,當(dāng)我們修改Model的接口時,我們不得不修改Controllers和Models。
但理想情況下,我們希望最小化受我們的更改影響的實體的數(shù)量,因此我們希望最小化它們間的依賴。
根據(jù)迪米特法則(Law of Demeter,又稱最少知道原則),我們希望一個角色對其他角色盡可能少的了解,它們之間通過友元類進行交互,而不是直接交互。
在這個基礎(chǔ)上,我們可以刪除View更改Model的能力,讓Controller來負(fù)責(zé)這個操作。

最初的MVC是不切實際的——尤其是在iOS
最初的MVC允許View和Controller的角色由同一對象實現(xiàn):
在簡單的情況下,Model、View和Controller角色可以由同一對象實現(xiàn)。
“In simple cases, the Model, View, and Controller roles may be played by the same object.”
UIViewController完全承擔(dān)了所有角色的談話者的職責(zé)。所以說,蘋果的MVC似乎遵循了最初的MVC思想,除了輸入大多來自UIControlls(屬于Views)而不是Controllers。
如果我們比較一下,它們看起來一樣:


一個對象有兩個職責(zé)導(dǎo)致我們將大多數(shù)業(yè)務(wù)邏輯保存在一個對象中。如果不及時發(fā)現(xiàn),這種方法可能最終出現(xiàn)臃腫的ViewControllers。雖然有一個獨立的Model實體,但是開發(fā)人員通常不知道Models是業(yè)務(wù)邏輯的主要維護者,并且不利用Model將其他實體完全分離。

從MVC中學(xué)習(xí)
如果我們把MVC看作是一個模式,而不是一個良好的架構(gòu)指南,它就變得非常有用。
事實上,MVC中有很多東西是“正確的”。
關(guān)注點的分離
MVC定義了三個角色或職責(zé),這是解決用戶與設(shè)備或應(yīng)用程序交互的問題的一個大概括。如果我們在抽象層面看而不是談?wù)搶崿F(xiàn)細節(jié),Model和View的職責(zé)非常清楚。同時,Controller的責(zé)任是值得商榷的,似乎是從處理用戶輸入的Model和View之間的中介。
Facade(外觀模式)
MVC使用一個Model來表示用戶對真實對象或現(xiàn)象的心理模型。Model包含了數(shù)據(jù)和如何更改數(shù)據(jù)的邏輯。這種邏輯通常稱為業(yè)務(wù)邏輯。人們往往忽略了邏輯部分,只把Models的數(shù)據(jù)變成對象,然后由Controllers操縱所有的邏輯,導(dǎo)致Models消瘦,Controllers臃腫。
適當(dāng)?shù)腗odels是對象和數(shù)據(jù)的集合,它們實際上是整個應(yīng)用程序的真實來源。
Facade是一個隱藏其他物體群的物體,就像建筑物的正面隱藏著里面的許多房間一樣。當(dāng)應(yīng)用外觀模式 時,這些對象的交互完全是通過Facade對象來完成的,就像人們從建筑物正面的入口進出建筑物一樣。我們避免直接訪問隱藏的對象,就像我們避免通過窗口跳躍以更快地進入房間一樣。

外觀模式確保了Models的可伸縮性。當(dāng)一些Model對象增長時,將大量的對象傳遞給Controllers,或者當(dāng)你注意到Controllers中大量的業(yè)務(wù)邏輯變得不方便時,考慮將這些Model對象封裝在一個Facade中,這樣就隱藏了復(fù)雜性和實現(xiàn)細節(jié)。
Facade阻止業(yè)務(wù)邏輯離開Model層并移動到Controller層。

Observer(觀察者模式)
從Models中移除依賴項的技術(shù)之一是觀察者模式。頂層的想法是,一個Model不知道誰使用它,只是通過通知或回調(diào)通知潛在用戶某事的發(fā)生或數(shù)據(jù)發(fā)生了變化。
理解觀察者模式最簡單的方法是想象一個對象,它利用無線電站傳播變化信號,其他對象使用收音機收聽信號。

當(dāng)您將Model層構(gòu)建為獨立的服務(wù) 時,這種技術(shù)就變得特別強大。
服務(wù)是有狀態(tài)對象,其生命周期通常與應(yīng)用程序的生命周期相等。例如網(wǎng)絡(luò)服務(wù)、特征服務(wù)、分析服務(wù)、聊天服務(wù)。
Controllers可以使用簡單的代碼來獲取到服務(wù)的狀態(tài)和更改。

Mediator(中介者模式)
我們來看看Controllers所扮演的角色:有了Models,也有了Views。我們還需要Controllers嗎?沒有Controllers將會導(dǎo)致我們直接從Views訪問Models,這將違反單一責(zé)任原則。
此外,我們很難編寫測試用例,因為Views依賴于平臺(UIKit等),每當(dāng)你想測試Views中的代碼時你都要考慮處理Views在UIViewController中的生命周期。
相反,我們在Models和Views之間設(shè)置Controllers作為中介。它們非常瘦,比Models和Views輕量,因為它們知道Models和Views,因此它們比Models或Views具有更多依賴。當(dāng)然,對象的依賴性越強,你要放進去的邏輯就應(yīng)當(dāng)越少。承擔(dān)較少的責(zé)任將減少復(fù)雜性,從而減少出錯的機會。
因此,Controllers就成為在Models和Views之間一個很輕但有時又臟的膠水(中介)。

由于我們的Model層通常是有狀態(tài)的,我們應(yīng)該盡可能的避免Controllers存儲狀態(tài),而是訪問存儲在Model層的數(shù)據(jù)然后進行計算。
但這并總是可行的。如果要實現(xiàn)業(yè)務(wù)邏輯所需的某些記帳方式很難放入相應(yīng)的服務(wù)/模型中,那么我們應(yīng)該選擇它們中弊端較小的做法,并在Controller中處理極端情況。
MVC的發(fā)展
讓我們看看現(xiàn)有的架構(gòu)中是如何分配MVC的角色的。
請確保你已經(jīng)讀過iOS架構(gòu)設(shè)計 這篇文章,以便我們能夠相互理解。
蘋果的MVC

顯然,View和Controller的角色由UIViewController擔(dān)當(dāng)。
MVP/MVVM


滿足了單一職責(zé)原則,每個實體都有相應(yīng)的作用。
VIPER/Riblets.

原本的VIPER/Riblets設(shè)計圖并沒有提到它必須有一個服務(wù)/存儲/存儲(Service/Repository/Storage) 將Entities和Interactor關(guān)聯(lián)起來作為Model層(我已經(jīng)添加到上面的圖)
我們至少有兩個Controllers:一個Presenter(提交者)和一個Router(路由器)。Presenter處理UI相關(guān)的邏輯,而Router負(fù)責(zé)模塊之間的通信。
總結(jié)
不要試圖將MVC作為一種單一的架構(gòu)設(shè)計模式,而是作為良好的應(yīng)用程序體系結(jié)構(gòu)的指導(dǎo)方針,或者一組可以解決我們一些問題的設(shè)計模式。
確保你不僅能夠在面試中解釋一種模式,而且要理解這種模式是如何解決作為程序員的日常生活中的實際問題的。
在開發(fā)過程中適當(dāng)?shù)卦O(shè)計模式(提升代碼質(zhì)量)。牢記YAGNI(you ain’t gonna need it,你不需要它)的原則,而不是選擇一個架構(gòu),然后把你的應(yīng)用程序硬塞到它的框架中。
避免盲目的將框架(比如說VIPER/Riblets)照搬到實際的項目中,需要考慮到實際業(yè)務(wù)的增長需求。死板的代碼會產(chǎn)生成噸的冗余。記住,我們總是可以在多個實體之間劃分角色,但是如果沒有必要,就避免這樣做。
設(shè)計模版的目的是使用戶能夠獲取信息或與系統(tǒng)交互,而不是讓開發(fā)人員偷懶。同時,避免生產(chǎn)“最終代碼”(明天不是世界末日)。你也許會把今天的代碼作為明天開發(fā)新功能的基礎(chǔ)。
感謝某位作者的這篇文章:extensive MVC investigation article [俄],它給了我寫這篇文章的靈感。
感謝你的閱讀,這篇文章來自??Do MVC like it’s 1979