前段時間,在使用了一段時間的MVVM架構以後,我從實際的項目中抽離出來,對使用MVVM架構的整個過程進行了總結,對於架構、對於編程思惟又有了不同的體會。因而提筆寫下本身探索MVVM架構的經驗和心得,以饗讀者。
本文會先對MVC架構作一個回顧,明確MVC中各層的職責;而後會提出MVVM架構的概念,原本接下來應該順勢舉幾個MVVM的例子進行說明的,可是考慮到響應式編程之於MVVM的重要性,因此在舉例以前會先講解一下響應式編程的概念(出於篇幅考慮,將MVVM架構實踐獨立成一篇文章,想直接看實例的請移駕這裏);最後會對MVC和MVVM的取捨談談本身的見解。話很少說,如今進入正題。
MVC架構
MVC(Model-View-Controller),是一種常見的客戶端軟件開發框架,具體到iOS上,絕大部分人從開始接觸iOS編程的時候都被告知MVC就是事實上的默認框架。系統也爲咱們實現好了公共的視圖類:UIView 和控制器類:UIViewController。大多數時候,咱們都須要繼承這些類來實現咱們的程序邏輯,所以,咱們幾乎逃避不開MVC這種設計模式。下面就對MVC各層的職責進行明確:
Model層
Model層 是服務端數據在客戶端的映射,是薄薄的一層,徹底能夠用struct表徵。下面看一個實例:
能夠看到,Model 層一般是服務端傳回的 JSON數據的映射,對應爲一個一個的屬性。不過如今也有不少人將網絡層(Service層)歸到Model中,也就是MVC(S)架構。同時,大部分時候數據的持久化操做也會放在Model層中。
總結一下,Model層的職責主要有如下幾項:
HTTP請求、進行字段驗證、持久化等。
View層
View層是展現在屏幕上的視圖的封裝,在 iOS 中也就是UIView以及UIView的子類。下面是UIView的繼承層級圖:
View層的職責是
展現內容和
接受用戶的操做與事件。
Controller層
看了Model層和View層如此簡單清晰的定義,若是你覺得接下來要講的Controller層的定義也跟這兩層同樣,那你就要失望了。
粗略總結了一下,Controller層的職責包括但不限於:
管理根視圖以及其子視圖的生命週期、
展現內容和佈局、
處理用戶行爲(如按鈕的點擊和手勢的觸發等)、
儲存當前界面的狀態(例如分頁加載的頁數、是否正在進行網絡請求的布爾值等)、
處理界面的跳轉、
做爲UITableView以及其它容器視圖的代理以及數據源、
業務邏輯和
各類動畫效果等。
畫風彷佛不對啊,爲何Controller層的職責比其餘兩層加起來還多?
MVC的困境
由於MVC架構中Controller層每每代碼不少,動輒二、3千行的這一特色,MVC也經常被調侃成是 Massive View Controller。形成這個問題的緣由就是MVC的定義太過簡單樸素,要知道支撐一個尚不算大的企業級應用都動輒幾十萬行代碼,還不包括各類依賴的第三方庫。這麼多的代碼如何安置?按照傳統的MVC定義,分割了小部分到Model層和View層,剩下的代碼都沒有其餘地方能夠去了,因而被通通的丟到了Controll層中。
龐大的Controller層帶來的問題就是難以維護、難以測試。並且其中充斥着大量的狀態值,一個任務的完成依賴於好幾個狀態值,而一個狀態值又同時參與到多個任務中,這樣複雜的多對多關係帶來的問題就是開發效率低下,須要花費大量的時間周旋在各個狀態值之間,對之後的功能拓展、業務添加也形成了障礙。
這樣的前提下,架構的改進就顯得很是有必要了。
MVVM架構初探
MVVM(Model-View-ViewModel),2005年由微軟的WPF和Silverlight的架構師 John Gossman 提出,是MVP模式與WPF結合發展演變過來的一種架構框架。MVVM實質上仍是MVC架構範圍,是一個精心優化的MVC架構,因此與MVC架構是兼容的。
MVVM首先將View層和Controller層進行了合併,統稱爲View層,由於View層和Controller層每每是一塊兒出現的。而後引入了一個新的模塊 —
ViewModel層,ViewModel層承載的內容就是以前在Controller層中
視圖展示邏輯。MVVM的圖示以下:
什麼是視圖展示邏輯呢?在一款應用中,數據的來源多是服務端返回、數據庫獲取和用戶輸入,而後存儲在Model中,可是這樣的數據是一種「未經格式化的」原始數據,還不能直接顯示到屏幕上。好比Model中可能有姓、名、暱稱等屬性,在某些界面中須要顯示成"姓名"的樣式,某些界面中顯示成"名姓"的樣式,某些界面中顯示"暱稱"的樣式。視圖展示邏輯就是把這些原始數據通過業務需求處理成展示到屏幕上的數據。能夠把一個應用當作是播出一個新聞節目,Model層就是一大堆繁雜的稿件,View層就是主持人實際播報的新聞,而ViewModel層就是幕後的編輯處理團隊,負責從凌亂的稿件中抽出須要的信息,整理成播報時用的稿件。這樣主持人拿着整理好的稿件,就能輕鬆的播報新聞了。
可是呢,無緣無故多了一個ViewModel層。多一個層帶來的直接問題就是信息的傳遞問題,層與層之間須要互通訊息,進行交流。在MVVM架構的實現中,開發人員想出了一個與傳統消息傳遞所不同的方式,這就引出了響應式編程的概念。
響應式編程
響應式編程(Reactive Programming),是一種面向數據流和變化傳播的範式。這意味着能夠在編程語言中很方便地表達靜態或動態的數據流,而相關的計算模型會自動將變化的值經過數據流進行傳播。舉個維基百科中的例子:c:=a+b表示將表達式的結果賦給c,而以後改變a或b的值不會影響c。但在響應式編程中,c的值會隨着a或b的更新而更新。
也就是說,
上圖中c的值最後會是5。
一樣的例子還有Excel中的單元格,單元格能夠包含字面值或相似"=B1+C1"的公式,而包含公式的單元格的值會依據其餘單元格的值的變化而變化 。
如何實現所謂的響應式編程?在WPF中官方提供了Data Binding技術,macOS中也有相似的Cocoa Binding框架,可是在iOS中官方沒有提供這樣的框架。因而GitHub上出現了ReactiveCococa(如下簡稱爲RAC)和RxSwift等優秀的第三方框架。
在RAC的思惟中,iOS上的一切都是在變化的數據流,好比輸入框上用戶正在不斷輸入的文字、被點擊的按鈕、旋轉縮放的視圖、不斷改變的NSString等等,這些就像是一個"水龍頭",當有變化產生的時候,水龍頭就會出水,把變化傳遞下去,對這個變化感興趣的人就能夠在這個水龍頭上套一個"水管",這我的就成爲了一個接收者(subscriber),當有變化產生的時候,接收者就能從水管中拿到這個變化的具體信息。
RAC就提供了這樣的"水管",可是和現實中的水管有所不一樣,RAC有本身的一些限制:水管中傳遞的不是水,而是一個個的"玻璃球",這些玻璃球的直徑和水管的內徑同樣大,保證了玻璃球在水管中都是依次排列經過的,這就保證了不會出現多個玻璃球並列經過的狀況。更加劇要的是,在拿到玻璃球以前,能夠對其進行一些個性化的定製。例如,能夠在水龍頭上加一個過濾嘴(filter),不符合的不讓經過;也能夠加一個改動裝置,把球改變成符合本身的需求(map);還能夠把多個水龍頭合併成一個新的水龍頭(combineLatest:reduce:),通過了這些定製以後,出來的符合要求的玻璃球就能拿來直接用了。
在這麼強大的框架幫助下,MVVM所引入的ViewModel層和其餘層之間的通信問題獲得瞭解決。
MVC仍是MVVM?
在考慮是否要選擇MVVM架構以前,先來總結一下MVVM的優點和不足。
MVVM主要有如下幾個優點:
-
Controller層瘦身:將視圖展示邏輯抽出到ViewModel層帶來的直接變化就是Controller層變得更加輕量級,更加容易維護。
-
更加容易測試:Controller層代碼減小了,也意味着對Controller層的測試更加容易。
-
兼容MVC:選擇MVVM並不意味着徹底摒棄MVC,MVVM至關因而MVC的超集,因此和MVC是兼容的,也就是說,能夠只在某一個模塊中使用MVVM,不用擔憂遷移架構時會形成須要全局重構的問題。
-
解決狀態以及狀態之間依賴過多的問題:這個優點是由RAC所帶來的,響應式編程關注的數據的變化和流向,免除了一部分的狀態,而是直接將變化傳到顯示的控件上,實例在這裏。
-
提供統一的消息傳遞機制:這也是由RAC所帶來的,RAC對iOS編程中大部分的實物進行了抽象,提供了統一的接口,因此能夠將iOS上KVO、通知(NSNotification)、委託(delegate)、Target-Action、塊(Block)等消息傳遞方式統一,實例在這裏。
MVVM存在的問題主要有:
-
學習曲線比較陡,一般須要引入第三方庫(ReactiveCocoa/RxSwift):使用MVVM一般須要引入第三方庫,並且須要轉換成響應式編程的思惟方式,這是須要花費至關的學習適應時間的。
-
建立更多的類:基本上每一個Controller類會對應有一個ViewModel類
- 性能上有必定影響,調用棧變深:RAC的實現底層依賴於KVO,帶來的問題是性能的損耗,好比光是subscribNext就慢了1個數量級,目前的回調堆棧也比較深,最簡單的[signal subscribeNext^(id x){}]就會有近40次的調用。
MVVM好處很多,缺點也一堆。那到底要不要用MVVM呢?我以爲,在項目還不算臃腫的時候,能夠簡單的對現有的MVC進行解耦優化,好比將網絡層(Service層)、持久層(Storage層)等部分抽象出來便可。另外一方面,MVVM對MVC也是兼容的,能夠考慮在項目中的某個模塊試水MVVM,以爲好再逐步替換其餘模塊;並且很重要的一點是,響應式編程這樣一種範式至關的鍛鍊咱們的編程思惟,讓咱們能夠站在數據的變化和流向的角度去思考咱們的整一個項目,掌握這種思惟方式也能夠反哺到咱們項目中別的地方。 架構沒有絕對的優劣,適合本身的架構就是最好的架構,那就讓咱們理性分析,擁抱變化。