如何通過學習舊的IOS架構來構建良好的IOS架構?
一些歷史
1979年,Trygve Reenskaug提出了MVC?——用戶控制龐大而復雜的數據集問題的一般解決方案。最初的論文引起了人們的極大興趣,最終,許多公司和個人提出了自己對MVC思想的理解和實現,他們并不局限于Model、View和Controller的原始定義。
最初的MVC
1979年的論文中對MVC的描述:
Models
Model代表認知,Model可以是單個對象(單一的),也可以是對象的某種結構。
“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是用戶和系統之間的鏈接。它為用戶提供輸入,安排相關View在屏幕上的適當位置顯示。
“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接收用戶的輸出,將其轉換為適當的消息,并將這些消息傳遞給一個或多個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永遠不應該補充Views,例如,不應該將它的View的節點用(繪制到屏幕上的)箭頭連接起來。
“A controller should never supplement the views, it should for example never connect the views of nodes by drawing arrows between them.”
所以Controller應該與視覺表現無關。
Controller連接到它的所有View,它們(View)作為Controller的部件。
“A controller is connected to all its views, they are called the parts of the controller.”
這意味著只要Controller創建了View,并且不是被注入的,那么它就可能知道Models。
...這個Editor非常類似于之前的Editor,但是它是在network中創建的。因此,該network及其所有activity的消息可以通過方法直接鍵入和執行。
“…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的舊名稱,并考慮到“網絡”和“活動”是Models,我們可以得出這樣的結論:Controller確實知道并更新Model。
Views
View被附加到它的Model(或Model部分),并從Model中獲取顯示所需的數據。它還可以通過發送適當的消息來更新Model。
“…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有兩個重大的問題:
- 它破壞了一些好的軟件設計規范。
- 在iOS中實現是不切實際的。
對MVC的改造
單一職責
Views和Controllers都負責更新模型,但我們希望只有一個實體有這樣的職責。這就是為什么大家提出單向數據流:
松耦合與高內聚
Views知道Models。Controllers知道Models和Views。這樣的話,Models就會依賴于Views和Controllers。這意味著,當我們修改Model的接口時,我們不得不修改Controllers和Models。
但理想情況下,我們希望最小化受我們的更改影響的實體的數量,因此我們希望最小化它們間的依賴。
根據迪米特法則(Law of Demeter,又稱最少知道原則),我們希望一個角色對其他角色盡可能少的了解,它們之間通過友元類進行交互,而不是直接交互。
在這個基礎上,我們可以刪除View更改Model的能力,讓Controller來負責這個操作。
最初的MVC是不切實際的——尤其是在iOS
最初的MVC允許View和Controller的角色由同一對象實現:
在簡單的情況下,Model、View和Controller角色可以由同一對象實現。
“In simple cases, the Model, View, and Controller roles may be played by the same object.”
UIViewController完全承擔了所有角色的談話者的職責。所以說,蘋果的MVC似乎遵循了最初的MVC思想,除了輸入大多來自UIControlls(屬于Views)而不是Controllers。
如果我們比較一下,它們看起來一樣:
一個對象有兩個職責導致我們將大多數業務邏輯保存在一個對象中。如果不及時發現,這種方法可能最終出現臃腫的ViewControllers。雖然有一個獨立的Model實體,但是開發人員通常不知道Models是業務邏輯的主要維護者,并且不利用Model將其他實體完全分離。
從MVC中學習
如果我們把MVC看作是一個模式,而不是一個良好的架構指南,它就變得非常有用。
事實上,MVC中有很多東西是“正確的”。
關注點的分離
MVC定義了三個角色或職責,這是解決用戶與設備或應用程序交互的問題的一個大概括。如果我們在抽象層面看而不是談論實現細節,Model和View的職責非常清楚。同時,Controller的責任是值得商榷的,似乎是從處理用戶輸入的Model和View之間的中介。
Facade(外觀模式)
MVC使用一個Model來表示用戶對真實對象或現象的心理模型。Model包含了數據和如何更改數據的邏輯。這種邏輯通常稱為業務邏輯。人們往往忽略了邏輯部分,只把Models的數據變成對象,然后由Controllers操縱所有的邏輯,導致Models消瘦,Controllers臃腫。
適當的Models是對象和數據的集合,它們實際上是整個應用程序的真實來源。
Facade是一個隱藏其他物體群的物體,就像建筑物的正面隱藏著里面的許多房間一樣。當應用外觀模式 時,這些對象的交互完全是通過Facade對象來完成的,就像人們從建筑物正面的入口進出建筑物一樣。我們避免直接訪問隱藏的對象,就像我們避免通過窗口跳躍以更快地進入房間一樣。
外觀模式確保了Models的可伸縮性。當一些Model對象增長時,將大量的對象傳遞給Controllers,或者當你注意到Controllers中大量的業務邏輯變得不方便時,考慮將這些Model對象封裝在一個Facade中,這樣就隱藏了復雜性和實現細節。
Facade阻止業務邏輯離開Model層并移動到Controller層。
Observer(觀察者模式)
從Models中移除依賴項的技術之一是觀察者模式。頂層的想法是,一個Model不知道誰使用它,只是通過通知或回調通知潛在用戶某事的發生或數據發生了變化。
理解觀察者模式最簡單的方法是想象一個對象,它利用無線電站傳播變化信號,其他對象使用收音機收聽信號。
當您將Model層構建為獨立的服務 時,這種技術就變得特別強大。
服務是有狀態對象,其生命周期通常與應用程序的生命周期相等。例如網絡服務、特征服務、分析服務、聊天服務。
Controllers可以使用簡單的代碼來獲取到服務的狀態和更改。
Mediator(中介者模式)
我們來看看Controllers所扮演的角色:有了Models,也有了Views。我們還需要Controllers嗎?沒有Controllers將會導致我們直接從Views訪問Models,這將違反單一責任原則。
此外,我們很難編寫測試用例,因為Views依賴于平臺(UIKit等),每當你想測試Views中的代碼時你都要考慮處理Views在UIViewController中的生命周期。
相反,我們在Models和Views之間設置Controllers作為中介。它們非常瘦,比Models和Views輕量,因為它們知道Models和Views,因此它們比Models或Views具有更多依賴。當然,對象的依賴性越強,你要放進去的邏輯就應當越少。承擔較少的責任將減少復雜性,從而減少出錯的機會。
因此,Controllers就成為在Models和Views之間一個很輕但有時又臟的膠水(中介)。
由于我們的Model層通常是有狀態的,我們應該盡可能的避免Controllers存儲狀態,而是訪問存儲在Model層的數據然后進行計算。
但這并總是可行的。如果要實現業務邏輯所需的某些記帳方式很難放入相應的服務/模型中,那么我們應該選擇它們中弊端較小的做法,并在Controller中處理極端情況。
MVC的發展
讓我們看看現有的架構中是如何分配MVC的角色的。
請確保你已經讀過iOS架構設計 這篇文章,以便我們能夠相互理解。
蘋果的MVC
顯然,View和Controller的角色由UIViewController擔當。
MVP/MVVM
滿足了單一職責原則,每個實體都有相應的作用。
VIPER/Riblets.
原本的VIPER/Riblets設計圖并沒有提到它必須有一個服務/存儲/存儲(Service/Repository/Storage) 將Entities和Interactor關聯起來作為Model層(我已經添加到上面的圖)
我們至少有兩個Controllers:一個Presenter(提交者)和一個Router(路由器)。Presenter處理UI相關的邏輯,而Router負責模塊之間的通信。
總結
不要試圖將MVC作為一種單一的架構設計模式,而是作為良好的應用程序體系結構的指導方針,或者一組可以解決我們一些問題的設計模式。
確保你不僅能夠在面試中解釋一種模式,而且要理解這種模式是如何解決作為程序員的日常生活中的實際問題的。
在開發過程中適當地設計模式(提升代碼質量)。牢記YAGNI(you ain’t gonna need it,你不需要它)的原則,而不是選擇一個架構,然后把你的應用程序硬塞到它的框架中。
避免盲目的將框架(比如說VIPER/Riblets)照搬到實際的項目中,需要考慮到實際業務的增長需求。死板的代碼會產生成噸的冗余。記住,我們總是可以在多個實體之間劃分角色,但是如果沒有必要,就避免這樣做。
設計模版的目的是使用戶能夠獲取信息或與系統交互,而不是讓開發人員偷懶。同時,避免生產“最終代碼”(明天不是世界末日)。你也許會把今天的代碼作為明天開發新功能的基礎。
感謝某位作者的這篇文章:extensive MVC investigation article [俄],它給了我寫這篇文章的靈感。
感謝你的閱讀,這篇文章來自??Do MVC like it’s 1979