前言
做客戶端開發(fā)、前端開發(fā)對MVC、MVP、MVVM這些名詞不了解也應該大致聽過,都是為了解決圖形界面應用程序復雜性管理問題而產(chǎn)生的應用架構(gòu)模式。
GUI程序所面臨的問題
圖形界面的應用程序提供給用戶可視化的操作界面,這個界面提供給數(shù)據(jù)和信息。用戶輸入行為(鍵盤,鼠標等)會執(zhí)行一些業(yè)務邏輯,可能會導致對應用程序數(shù)據(jù)的變更,數(shù)據(jù)的變更自然需要用戶界面的同步變更以提供最準確的信息。例如用戶對一個電子表格重新排序的操作,應用程序需要響應用戶操作,對數(shù)據(jù)進行排序,然后需要同步到界面上。
在開發(fā)應用程序的時候,以求更好的管理應用程序的復雜性,基于職責分離(Speration of Duties)的思想都會對應用程序進行分層。在開發(fā)圖形界面應用程序的時候,會把管理用戶界面的層次稱為View,應用程序的數(shù)據(jù)為Model(注意這里的Model指的是Domain Model,這個應用程序?qū)π枰鉀Q的問題的數(shù)據(jù)抽象,不包含應用的狀態(tài),可以簡單理解為對象)。Model層對應用程序的業(yè)務邏輯無知,只保存數(shù)據(jù)結(jié)構(gòu)和提供數(shù)據(jù)操作的接口
有了View和Model的分層,那么就有了兩個問題:
1、響應用戶操作的業(yè)務邏輯(例如排序)的管理。
2、View如何同步Model的變更。
帶著這兩個問題開始探索MV模式,會發(fā)現(xiàn)這些模式之間的差異可以歸納為對這兩個問題處理的方式的不同。而幾乎所有的MV模式都是經(jīng)典的Smalltalk-80 MVC的修改版。
MVC的依賴關(guān)系
MVC出了把應用程序分成View、Model層,還額外的加了一個Controller層,它的職責就是專門管理應用程序的業(yè)務邏輯。Model、View、Controller三個層次的依賴關(guān)系如下:
Controller和View都依賴Model層,Controller和View可以互相依賴。在一些網(wǎng)上的資料Controller和View之間的依賴關(guān)系可能不一樣,有些是單向依賴,有些是雙向依賴,這個其實關(guān)系不大,后面會看到它們的依賴關(guān)系都是為了把處理用戶行為觸發(fā)的業(yè)務邏輯的處理權(quán)交給Controller。
MVC的調(diào)用關(guān)系
用戶的對View操作以后,View捕獲到這個操作,會把處理的權(quán)利交移給Controller(Pass calls);Controller接著會執(zhí)行相關(guān)的業(yè)務邏輯,這些業(yè)務邏輯可能需要對Model進行相應的操作;當Model變更了以后,會通過觀察者模式(Observer Pattern)通知View;View通過觀察者模式收到Model變更的消息以后,會向Model請求最新的數(shù)據(jù),然后重新更新界面。如下圖:
看似沒有什么特別的地方,但是由幾個需要特別關(guān)注的關(guān)鍵點:
1、View是把控制權(quán)交移給Controller,自己不執(zhí)行業(yè)務邏輯。
2、Controller執(zhí)行業(yè)務邏輯并且操作Model,但不會直接操作View,可以說它是對View無知的。
3、View和Model的同步消息是通過觀察者模式進行,而同步操作是由View自己請求Model的數(shù)據(jù)然后對視圖進行更新。
需要特別注意的是MVC模式的精髓在于第三點:Model的更新是通過觀察者模式告知View的,具體表現(xiàn)形式可以是Pub/Sub或者是觸發(fā)Events。而網(wǎng)上很多對于MVC的描述都沒有強調(diào)這一點。通過觀察者模式的好處就是:不同的MVC三角關(guān)系可能會有共同的Model,一個MVC三角中的Controller操作了Model以后,兩個MVC三角的View都會接受到通知,然后更新自己。保持了依賴同一塊Model的不同View顯示數(shù)據(jù)的實時性和準確性。我們每天都在用的觀察者模式,在幾十年前就已經(jīng)被大神們整合到MVC的架構(gòu)當中。
MVC的優(yōu)缺點
優(yōu)點:
1、把業(yè)務邏輯全部分離到Controller中,模塊化程度高。當業(yè)務邏輯變更的時候,不需要變更View和Model,只需要Controller換成另外一個Controller就行了(Swappable Controller)。
2、觀察者模式可以做到多視圖同時更新。
缺點:
1、Controller測試困難。因為視圖同步操作是由View自己執(zhí)行,而View只能在有UI的環(huán)境下運行。在沒有UI環(huán)境下對Controller進行單元測試的時候,Controller業(yè)務邏輯的正確性是無法驗證的:Controller更新Model的時候,無法對View的更新操作進行斷言。
2、View無法組件化。View是強依賴特定的Model的,如果需要把這個View抽出來作為一個另外一個應用程序可復用的組件就困難了。因為不同程序的的Domain Model是不一樣的
MVP
1、Passive View
MVP(Passive View)的依賴關(guān)系
MVP模式把MVC模式中的Controller換成了Presenter。MVP層次之間的依賴關(guān)系如下:
MVP打破了View原來對于Model的依賴,其余的依賴關(guān)系和MVC模式一致。
MVP(Passive View)的調(diào)用關(guān)系
既然View對Model的依賴被打破了,那View如何同步Model的變更?看看MVP的調(diào)用關(guān)系:
和MVC模式一樣,用戶對View的操作都會從View交移給Presenter。Presenter同樣的會執(zhí)行相應的業(yè)務邏輯,并且對Model進行相應的操作;而這時候Model也是通過觀察者模式把自己變更的消息傳遞出去,但是是傳給Presenter而不是View。Presenter獲取到Model變更的消息以后,通過View提供的接口更新界面。
關(guān)鍵點:
1、View不再負責同步的邏輯,而是由Presenter負責。Presenter中既有業(yè)務邏輯也有同步邏輯。
2、View需要提供操作界面的接口給Presenter進行調(diào)用。(關(guān)鍵)
對比在MVC中,Controller是不能操作View的,View也沒有提供相應的接口;而在MVP當中,Presenter可以操作View,View需要提供一組對界面操作的接口給Presenter進行調(diào)用;Model仍然通過事件廣播自己的變更,但由Presenter監(jiān)聽而不是View。
MVP(Passive View)的優(yōu)缺點
優(yōu)點:
1、便于測試。Presenter對View是通過接口進行,在對Presenter進行不依賴UI環(huán)境的單元測試的時候。可以通過Mock一個View對象,這個對象只需要實現(xiàn)了View的接口即可。然后依賴注入到Presenter中,單元測試的時候就可以完整的測試Presenter業(yè)務邏輯的正確性。這里根據(jù)上面的例子給出了Presenter的單元測試樣例。
2、View可以進行組件化。在MVP當中,View不依賴Model。這樣就可以讓View從特定的業(yè)務場景中脫離出來,可以說View可以做到對業(yè)務邏輯完全無知。它只需要提供一系列接口提供給上層操作。這樣就可以做高度可復用的View組件。
缺點:
1、Presenter中除了業(yè)務邏輯以外,還有大量的View->Model,Model->View的手動同步邏輯,造成Presenter比較笨重,維護起來會比較困難。
MVVM
ViewModel
MVVM代表的是Model-View-ViewModel,這里需要解釋一下什么是ViewModel。ViewModel的含義就是 "Model of View",視圖的模型。它的含義包含了領域模型(Domain Model)和視圖的狀態(tài)(State)。 在圖形界面應用程序當中,界面所提供的信息可能不僅僅包含應用程序的領域模型。還可能包含一些領域模型不包含的視圖狀態(tài),例如電子表格程序上需要顯示當前排序的狀態(tài)是順序的還是逆序的,而這是Domain Model所不包含的,但也是需要顯示的信息。
可以簡單把ViewModel理解為頁面上所顯示內(nèi)容的數(shù)據(jù)抽象,和Domain Model不一樣,ViewModel更適合用來描述View。
MVVM的依賴
MVVM的依賴關(guān)系和MVP依賴,只不過是把P換成了VM。
MVVM的調(diào)用關(guān)系
MVVM的調(diào)用關(guān)系和MVP一樣。但是,在ViewModel當中會有一個叫Binder,或者是Data-binding engine的東西。以前全部由Presenter負責的View和Model之間數(shù)據(jù)同步操作交由給Binder處理。你只需要在View的模版語法當中,指令式地聲明View上的顯示的內(nèi)容是和Model的哪一塊數(shù)據(jù)綁定的。當ViewModel對進行Model更新的時候,Binder會自動把數(shù)據(jù)更新到View上去,當用戶對View進行操作(例如表單輸入),Binder也會自動把數(shù)據(jù)更新到Model上去。這種方式稱為:Two-way data-binding,雙向數(shù)據(jù)綁定。可以簡單而不恰當?shù)乩斫鉃橐粋€模版引擎,但是會根據(jù)數(shù)據(jù)變更實時渲染。
也就是說,MVVM把View和Model的同步邏輯自動化了。以前Presenter負責的View和Model同步不再手動地進行操作,而是交由框架所提供的Binder進行負責。只需要告訴Binder,View顯示的數(shù)據(jù)對應的是Model哪一部分即可。
MVVM的優(yōu)缺點
優(yōu)點:
1、提高可維護性。解決了MVP大量的手動View和Model同步的問題,提供雙向綁定機制。提高了代碼的可維護性。
2、簡化測試。因為同步邏輯是交由Binder做的,View跟著Model同時變更,所以只需要保證Model的正確性,View就正確。大大減少了對View同步更新的測試。
缺點:
1、過于簡單的圖形界面不適用,或說牛刀殺雞。
2、對于大型的圖形應用程序,視圖狀態(tài)較多,ViewModel的構(gòu)建和維護的成本都會比較高。
3、數(shù)據(jù)綁定的聲明是指令式地寫在View的模版當中的,這些內(nèi)容是沒辦法去打斷點debug的。
結(jié)語
可以看到,從MVC->MVP->MVVM,就像一個打怪升級的過程。后者解決了前者遺留的問題,把前者的缺點優(yōu)化成了優(yōu)點。同樣的Demo功能,代碼從最開始的一堆文件,優(yōu)化成了最后只需要20幾行代碼就完成。MV*模式之間的區(qū)分還是蠻清晰的,希望可以給對這些模式理解比較模糊的同學帶來一些參考和思路。