原文地址:https://linux.cn/article-6481-1.html
希望有人能懂,把mvc文章放設計模式分類下的用意。
前言
做客戶端開發、前端開發對MVC、MVP、MVVM這些名詞不了解也會大致聽過,都是為了解決圖形界面應用程序復雜性管理問題而產生的應用架構模式。
網上很多文章都是關于這方面的討論比較雜亂,各種MV*模式之前的區別不輕,甚至有些描述都是錯誤的。
本文追根溯源,從最經典的Smalltalk-80 MVC模式開始逐步還原圖形界面之下最真實的MV*模式。
GUI程序所面臨的問題
圖形界面的應用程序提供給用戶可視化的界面操作,這個界面提供給數據和信息。用戶輸入行為(鍵盤、鼠標等)會執行一些應用邏輯,
應用邏輯可能會觸發一定的業務邏輯對應用程序數據的變更,數據的變更自然需要用戶界面的同步變更以提供最準確的信息。 例如用戶對一個電子表格重新排序的操作,應用程序需要相應用戶操作,對數據進行排序,然后需要同步界面上。
在開發應用程序的時候,以求更好的管理應用程序的復雜性,基于職責分離的思想都會對應用進行分層。
在開發圖形界面應用程序的時候,會把管理用戶界面的層次稱為View,應用程序的數據為Model(注意:助理的Model指的是Domain Model,這個應用程序對需要解決的問題的數據抽象,不包含應用的狀態,可以簡單理解為對象)。Model提供數據操作的接口,執行相應的業務邏輯。
有了View和Model的分層,那么問題就來了:View如果同步Model的變更,View和Model之間如何粘合在一起。
帶著這個問題開始探索MV*模式,會發現這些模式之前的差異可以歸納為對這個問題處理的方式的不同。而幾乎所有的MV*模式都是經典的Smalltalk-80 MVC的修訂版。
Smalltalk-80 MVC
歷史背景
早在上個世紀70年代,美國的施樂公司(Xerox)的工程師研發了Smalltakl編程語言,并且開始用它編寫圖形界面的應用程序。而在Smalltalk-80這個版本的時候,以為叫Trygve Reenskaug的工程師設計了MVC圖形應用程序的架構模式,極大的降低了圖形應用程序的管理難度。
而在四人幫(GoF)的設計模式當中并沒有把MVC當做是設計模式,而僅僅把它看做解決問題的一類問題的集合。
Smalltalk-80 MVC和GoF描述的MVC是最經典的MVC模式。
MVC的依賴關系
MVC除了把應用程序分成View、Model層,還額外的增加了一個Controller層,它的職責為進行Model和View之間的協作(路由、輸入預處理)的應用邏輯;Model進行處理業務邏輯。Model、View、Controller三個層次的依賴關系如下:
Controller和View都依賴Model層,Controller和View層可以相互依賴。在一些網上的資料Controller和View之前的依賴關系可能不一樣,有些是單向依賴,有些是雙向依賴,這個其實關系不大,后面會看到他們的依賴關系都是為了把處理用戶行為觸發的事件處理權交給Controller。
MVC 的調用關系
用戶的對View操作之后、View捕獲到這個操作,會把處理的權利交移給Controller;Controller會對來自View的數據進行預處理、決定調用Model的接口;然后由Model執行相關的業務邏輯;當Model變更了以后,會通過觀察者模式通知View;View通過觀察者模式收到Model變更的消息以后,會向Model請求最新的數據,然后重新更新界面。如下圖:
看似沒有什么特別的地方,但是有幾個需要特別關注的關鍵點:
1、View是把控制權交移給Controller,Controller執行應用程序相關的應用邏輯(對來自View數據進行預處理,決定調用哪個Model的接口等)。
2、Controller操作Model,對Model執行業務邏輯對數據進行處理。但不會直接操作View,可以說它是對View無知的。
3、View和Model的同步消息是通過觀察者模式進行,而同步操作是由View自己請求Model的數據然后對其視圖進行更新。
需要特別注意的是MVC模式的精髓在于第三點;Model的更新是通過觀察者模式告知View的,具體表現形式可以是Pub/Sub或者觸發Events。而網上很多對于MVC的描述都沒有強調這一點。通過觀察者模式的好處就是:不同的MVC三角關系可能會有共同的Model,一個MVC三角中的Controller操作了Model以后,兩個MVC三角的View都會接收到通知,然后更新自己。保持了依賴同一塊Model的不同View顯示數據的實時性和準確性。
我們每天都在用的觀察者模式,在幾十年前就已經被大神們整合到MVC架構當中。
MVC 的優缺點
優點:
1、把業務邏輯和展示邏輯分離,模塊化程度高。且當應用邏輯需要變更的時候,不需要變更業務和展示邏輯,只需要Controller換成另外一個Controller就行了。
2、觀察者模式可以做到多數圖同時更新。
缺點:
1、Controller測試困難,因為視圖同步操作是由View自己執行,而View只能在有UI的環境下運行。在沒有UI環境下對Controller進行單元測試的時候,應用邏輯正確性是無法驗證的:Model更新的時候,無法對View的更新操作進行斷言。
2、View無法組件化。View是強依賴特定的Model的,如果需要把這個View抽出來作為另外一個應用的可復用組件就困難了。因為不同程序的Domain Model是不一樣的。
MVC Model 2
在Web服務端開發的時候也會接觸到MVC模式,而這種MVC模式不能嚴格成為MVC模式。經典的MVC模式只是解決客戶端圖形界面應用程序的問題,而對服務端無效。
服務端的MVC模式有自己特定的名字:MVC Model2,或者叫做JSP Model2。Model2客戶端服務端的交互模式如下:
服務端接收到來自客戶端的請求,服務端通過路由規則把這個請求交由特定的Controller進行處理,Controller執行相應的應用邏輯,對Model進行操作,Model執行業務邏輯以后;然后用數據去渲染特定的模板,返回給客戶端。
因為HTTP協議是單工協議并且是無狀態的,服務端無法直接給客戶端推送數據。除非客戶端再次發起請求,否則服務端的Model的變更就無法告知客戶端。所以看到經典的Smalltalk-80 MVC中的Model通過觀察者模式告知View更新這一環被無情的打破,不能成為嚴格的MVC。
Model 2模式最早在1998年應用在JSP應用程序當中,JSP Model 1應用管理的混亂誘發了JSP參考了客戶端MVC模式,催生了Model 2.
后來這種模式幾乎被應用在所有語言的Web開發框架中。PHP的ThinkPHP,Python的Dijango、Flask,NodeJS的Experss,Ruby的ROR,基本上都采用了這種模式。
平常講的MVC基本上是這種服務端的MVC。
MVP
MVP模式有兩種:
1、Passive View
2、Supervising Controller
而大多數情況下討論的都是Passive View模式。本文會對PV模式進行較為詳細的介紹,而SC模式則簡單提及。
歷史背景
MVP模式是MVC模式的改良。在上個世紀90年代,IBM旗下的子公司Taligent在用C/C++開發一個叫CommonPoint的圖形界面應用系統提出來的。
MVP(Passive View)的依賴關系
MVP模式把MVC模式中的Controller換成了Presenter。MVP層次之前的依賴關系如下:
MVP 打破了原來View對于Model的依賴,其余的依賴關系和MVC模式一致。
MVP(Passive View)的依賴關系
既然View對Model的依賴被打破了,那View如何同步Model的變更?看看MVP的調用關系:
和MVC模式一樣,用戶對于View的操作都會從View交移給Presenter。
Presenter會執行相應的應用程序邏輯,并且對Model進行相應的操作;而這時候Model執行完業務邏輯以后,也是通過觀察者模式把自己變更的消息傳遞出去,但是傳給Presenter而不是View。Presenter獲取到Model變更的消息以后,通過View提供的接口更新界面。
關鍵點:
1、View不再負責同步的邏輯,而是由Presenter負責。Presenter中既有應用程序邏輯也有同步邏輯。
2、View需要提供操作界面的接口給Presenter進行調用。(關鍵)
對比在MVC中,Controller是不能操作View的,View也沒有提供相應的接口;而在MVP當中,Presenter可以操作View,View需要提供一組對界面操作的借口給Presenter進行調用;Model仍然通過事件廣播自己的變更,但由Presenter監聽而不是View。
MVP (Passive Sive)的優缺點
優點:
1、便于測試。Persenter對View通過接口進行,在對Presenter進行不依賴UI環境的單元測試的時候??梢酝ㄟ^Mock一個View對象,這個對象只需要實現View的接口即可。然后通過依賴注入到Presenter中,單元測試的時候就可以完整的測試Presenter應用程序的正確性。
2、View可以進行組件化。在MVP當中,View不依賴Model。這樣就可以讓View從特定的業務場景中脫離出來,可以說View可以做到對業務完全無知。它只需要提供一系列接口給上層操作。這樣就可以高度可復用的View組件。
缺點:
1、Presenter中除了應用邏輯之外,還有大量的View-> Model,Model->View的手動同步邏輯,造成Presenter比較笨重,維護起來會比較困難。
MVP(SuperVising Controller)
上面講到的MVP的Passive View模式,該模式下的View非常Passive,它幾乎什么都不知道,Presenter讓它干什么就干什么。
而Supervising Controller模式中,Presenter會把一部分簡單的同步邏輯交給View自己去做,Presenter之負責比較復雜的、高層次的UI操作,所以可以把它看成一個Supervising Controller。
Supervising Controller模式下的依賴和調用關系:
因為Supervising Controller用得比較少,討論就到這里。
MVVM
MVVM可以看做可以一宗特殊的MVP(Passive View)模式,或者說對MVP模式的一種改良。
歷史背景
MVVM模式最早是微軟公司提出,并且大量使用在.NET的WPF和Sliverlight中。2005年微軟工程師John Gossman在自己的博客中首次公布了MVVM模式。
ViewModel
MVVM代表的是Model-View-ViewModel,這里需要解釋一下什么是ViewModel。ViewModel的含義就是“Model of View”,視圖的模型。
它的含義包含了領域模型和視圖狀態。
在圖形界面應用程序當中,界面所提供的信息可能不僅僅包含應用程序的領域模型,還可能包含一些領域模型不包含的視圖狀態,例如電子表格程序上需要顯示當前排序狀態是順序還是逆序的,而這是Domain Model不包含的,但是也是需要顯示的信息。
可以簡單把ViewModel理解為頁面上所顯示內容的數據抽象,和Domain Model不一樣,VIewModel更適合用來描述View。
MVVM的依賴
MVVM的依賴關系和MVP依賴基本一致,只不過是把P換成了VM。
MVVM的調用關系
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的優缺點
優點:
1、提高可維護性。解決了MVP大量的手動同步問題,提供雙向綁定機制。提高代碼可維護性。
2、可測試性。以為同步邏輯交由Binder做的,View跟著Model同時變更,所以只需要保證Model的正確性,View就正確。大大較少了對View同步更新的測試。
缺點:
1、過于簡單的圖形界面不適用,殺雞不需用牛刀。
2、對于大型的圖形應用程序,視圖狀態較多,ViewModel的構建和維護的成本都會比較高。
3、數據綁定的聲明是指令式地寫在View的模板當中的。這些內容是沒辦法去打斷點Debug的。
結語
可以看到,從MVC->MVP->MVVM,就像一個打怪升級的過程,后者解決了前者遺留的問題,把前者缺點優化成優點。
同樣的工程文件,代碼從最開始的一堆文件,優化成了最后只需要20幾行代碼就完成。MV*模式之前的區分還是蠻清晰的,希望可以給這些模式理解比較模糊的同學帶來一些參考和思路。