再談MV*(MVVM MVP MVC)模式的設計原理—封裝與解耦
精煉並增補於:介面之下:還原真實的MV*模式
圖形介面的應用程式提供給使用者視覺化的操作介面,這個介面提供給資料和資訊。使用者輸入行為(鍵盤,滑鼠等)會執行一些應用邏輯,應用邏輯(application logic)可能會觸發一定的業務邏輯(business logic)對應用程式資料的變更,資料的變更自然需要使用者介面的同步變更以提供最準確的資訊。
在開發應用程式的時候,以求更好的管理應用程式的複雜性,基於職責分離(Speration of Duties)的思想都會對應用程式進行分層。在開發圖形介面應用程式的時候,會把管理使用者介面的層次稱為View,應用程式的資料為Model(注意這裡的Model指的是Domain Model,這個應用程式對需要解決的問題的資料抽象,不包含應用的狀態,可以簡單理解為物件)。Model提供資料操作的介面,執行相應的業務邏輯。
有了View和Model的分層,那麼問題就來了:View如何同步Model的變更,View和Model之間如何粘合在一起?
MV*模式解決什麼問題
MV*就是實現了領域模型資料和UI層的解耦。
MVC、MVP、MVVM對其解耦的思路的不同。從歷史的角度來看,MVC、MVP和MVVM是一種進化的關係。但是鑑於專案的規模以及模式實現的方式不同,不同的MV*模式各有其優點和缺點,難分孰好孰壞。
但是業界越來越認為:MVVM是前端領域最好的MV*模式。Angular、Vue是MVVM模式典範
MVC的依賴關係
MVC出了把應用程式分成View、Model層,還額外的加了一個Controller層,職責為進行Model和View之間的協作(路由、輸入預處理等)的應用邏輯(application logic)。
-
Model主要是與業務資料有關。
-
View是應用程式資料的視覺化表示。
-
Controller管理應用程式中Model和View之間的邏輯和協調。
使用者對View的輸入等操作並不會在View的相關模組中處理邏輯,而是由Controller層獲得這些操作(所謂的Pass Call),並由Controller層對這些操作中的資料經過應用邏輯的操作,然後在呼叫Model層的介面,將資料交給Model層。Model層執行與業務邏輯相關的操作,並更新資料。Model和View通過觀察者模式聯絡在一起,即View是Model的觀察者,當Model資料變動之後,通知View層進行資料更新。
MVC優點
-
把業務邏輯全部分離到Controller中,模組化程度高。當業務邏輯變更的時候,不需要變更View和Model,只需要Controller換成另外一個Controller就行了(Swappable Controller)。
-
觀察者模式可以做到多檢視同時更新。
MVC缺點
-
Controller測試困難。因為檢視同步操作是由View自己執行,而View只能在有UI的環境下執行。在沒有UI環境下對Controller進行單元測試的時候,Controller業務邏輯的正確性是無法驗證的:Controller更新Model的時候,無法對View的更新操作進行斷言。
-
View無法元件化。View是強依賴特定的Model的,如果需要把這個View抽出來作為一個另外一個應用程式可複用的元件就困難了。因為不同程式的的Domain Model是不一樣的
MVP模式
MVP比起MVC模式,它的特點很明顯。MVP中M和V之間的依賴關係被消除了。
在MVC中,M和V之間通過觀察者模式依賴。這種依賴關係在MVP中被轉移到M和P層中。這樣一來P層必須通過一定的機制通知V層進行資料的更新。所以MVP模式中V層中提供了供P層呼叫的介面。P層作為觀察者獲得資料變化是,將呼叫V層的介面將變化反映到V層中。
在MVP中:
-
Model層依然是主要與業務資料有關。、
-
View依然是應用程式的視覺化表示,但是在MVP中它對領域資料(Model層)完全無知,View不再負責同步的邏輯,而是由Presenter負責。Presenter中既有應用程式邏輯也有同步邏輯。所以比起MVC中View層更輕了。但是,View需要提供操作介面的介面給Presenter進行呼叫
-
Presenter層比較重,它不僅呼叫Model的介面,也呼叫View的介面。而且需要作為觀察者獲得Model的資料更新。
MVP(Passive View)的呼叫關係
MVP(Passive View)優點
-
便於測試。Presenter對View是通過介面進行,在對Presenter進行不依賴UI環境的單元測試的時候。可以通過Mock一個View物件,這個物件只需要實現了View的介面即可。然後依賴注入到Presenter中,單元測試的時候就可以完整的測試Presenter應用邏輯的正確性。這裡根據上面的例子給出了Presenter的單元測試樣例。
-
View可以進行元件化。在MVP當中,View不依賴Model。這樣就可以讓View從特定的業務場景中脫離出來,可以說View可以做到對業務完全無知。它只需要提供一系列介面提供給上層操作。這樣就可以做到高度可複用的View元件。
MVP(Passive View)缺點
-
Presenter中除了應用邏輯以外,還有大量的View->Model,Model->View的手動同步邏輯,造成Presenter比較笨重,維護起來會比較困難。
MVP(Supervising Controller)
Supervising Controller模式中,Presenter會把一部分簡單的同步邏輯交給View自己去做,Presenter只負責比較複雜的、高層次的UI操作,所以可以把它看成一個Supervising Controller。
因為Supervising Controller用得比較少,MVVM可以看作是一種特殊的MVP(Passive View)模式,或者說是對MVP模式的一種改良。
MVVM的依賴
Model-View-ViewModel模式中,M層資料的變化不是通過觀察者模式通知到V層的(即沒有M和V的依賴),也不是通過VM層呼叫V層的介面將資料傳遞給V層的(這意味著使用者程式碼不需要手動更新V層)。而是通過在VM層實現一個特殊的binder,將資料從M層直接繫結到V層。這樣ViewModel層瞭解Model層,View層瞭解ViewModel層。
ViewModel充當了一個數據轉換器的作用。它將Model資訊轉換為View資訊,還將命令從View傳遞到Model。在這裡,View可以訪問ViewModel,ViewModel可以訪問Model。
MVVM的呼叫關係和MVP一樣。但是,在ViewModel當中會有一個叫Binder,或者是Data-binding engine的東西。以前全部由Presenter負責的View和Model之間資料同步操作交由給Binder處理。你只需要在View的模版語法當中,指令式地宣告View上的顯示的內容是和Model的哪一塊資料繫結的。當ViewModel對進行Model更新的時候,Binder會自動把資料更新到View上去,當用戶對View進行操作(例如表單輸入),Binder也會自動把資料更新到Model上去。這種方式稱為:Two-way data-binding,雙向資料繫結。可以簡單而不恰當地理解為一個模版引擎,但是會根據資料變更實時渲染。
MVVM把View和Model的同步邏輯自動化了。以前Presenter負責的View和Model同步不再手動地進行操作,而是交由框架所提供的Binder進行負責。只需要告訴Binder,View顯示的資料對應的是Model哪一部分即可。
MVVM優點
-
雙向繫結技術,當Model變化時,View-Model會自動更新,View也會自動變化。很好做到資料的一致性,不用擔心,在模組的這一塊資料是這個值,在另一塊就是另一個值了。所以 MVVM模式有些時候又被稱作:model-view-binder模式。
-
提高可維護性。解決了MVP大量的手動View和Model同步的問題,提供雙向繫結機制。提高了程式碼的可維護性。
-
簡化測試。因為同步邏輯是交由Binder做的,View跟著Model同時變更,所以只需要保證Model的正確性,View就正確。大大減少了對View同步更新的測試。
MVVM缺點
-
過於簡單的圖形介面不適用,或說牛刀殺雞。
-
對於大型的圖形應用程式,檢視狀態較多,ViewModel的構建和維護的成本都會比較高。
-
資料繫結的宣告是指令式地寫在View的模版當中的,這些內容是沒辦法去打斷點debug的。
-
一個大的模組中model也會很大,雖然使用方便了也很容易保證了資料的一致性,當時長期持有,不釋放記憶體就造成了花費更多的記憶體。
-
資料雙向繫結不利於程式碼重用。客戶端開發最常用的重用是View,但是資料雙向繫結技術,讓你在一個View都綁定了一個model,不同模組的model都不同。那就不能簡單重用View了。