大概是二三十年前, 人類逐漸從命令行界面時代走出來,進化到了GUI時代。
注: GUI(Graphic User Interface),即圖形用戶接口。
(一個命令行程序)
(一個帶有圖形界面的桌面應用程序 ,自己畫的,有點醜啊)
每當人類努力地開發新的桌面GUI程序的時候, 至少要搞定下面幾類工作:
1. 界面(以及界面中元素的)佈局。
這是一件挺費勁的工作, 要儘可能地美觀漂亮,要不然就賣不出去。
2. 界面上有些“邏輯”需要處理
比如上圖中那個薪水計算程序,“計算” 按鈕默認是灰色的, 不能點擊,用戶輸入了稅前收入以後, “計算”按鈕就會被激活,表示計算了。
3. 所謂的業務邏輯。
用戶點擊了“計算”按鈕以後,計算五險一金,個人所得稅和稅後收入。
這三者攪在一起,讓程序代碼凌亂不堪,稍微複雜點兒的程序就長達幾千行, 不斷地挑戰著程序員的底線,修改別人的代碼,添加新的功能要比從頭寫難好多倍!
大家都在泥潭中掙扎。
桌面應用程序的 MVC
程序越來越複雜,Bug越來越多,沒辦法, 大家只好去求編程上帝。
上帝說: 想從困境中走出來,一定要實現關注點分離(Separation of concerns)。
沒人能夠理解。
上帝解釋道:“ 你們人腦同時能處理的東西是有限的, 所以要把一個大系統給分解,變成幾個相對獨立的部分,這樣我們的大腦每次只關注某一方面,暫時忽略其他的,就能夠掌控了。”
沒人知道該怎麼分解。
上帝只好想了一個辦法, 把關注點分離的理論給具體化,這個辦法就是MVC。
上帝告訴人類:
M 表示 Model , 專門用來處理業務邏輯,不幹別的事情。
例如在那個薪水計算系統中。計算一個人的薪水,五險一金,個人所得稅等等。
V 表示View, 專注頁面佈局和數據顯示。
例如把Button放置到某個位置,把總收入顯示到一個文本框,把稅金顯示到另外一個地方。
C 表示Controller 翻譯用戶的輸入,操作模型和視圖。
例如,用戶在界面點擊了一個“計算”的按鈕,View 把計算的請求傳遞給Controller (很明顯View需要知道Controller,換句話說,需要持有Controller的實例),Controller找到或者創建Model,執行業務邏輯:計算薪水。
計算的結果該怎麼展示呢? 人類問道。
上帝胸有成竹: 可以讓Model 去通知View。
Model需要持有View的實例(當然也可以通過觀察者模式),調用View對應的方法。
例如: View中可能有一個onResult的方法, 讓Model去調用,在調用的時候把一個參數對象Salary傳遞過來,不就可以展示數據了嗎?
畫成流程圖的話是這個樣子:
大家都覺得MVC大法好,紛紛開始使用。
MVP
時間久了以後,人類就覺得不爽了,因為在這個MVC中,依賴太多:
View 依賴Controller和Model
Controller依賴View和Model
Model 和View的關係雖然很弱, 但是也需要某種方式來通知View進行數據更新。
人類說:“他們之間的耦合還是挺緊密的啊,親愛的上帝,能不能改改?”
上帝覺的人類還是挺有上進心的,決定繼續施以援手: “這樣吧, 可以改變一下Controller, 把Model和View完全隔離開,讓他們單獨變化。”
上帝把Controller 改了個名稱,叫做Presenter, 把整體命名為MVP。
在MVP當中,View只知道Presenter, 不知道Model 。
計算流程和MVC差不多,用戶點擊了“計算薪水”按鈕, View去調用Presenter, Presenter操作Model , Model 中進行業務計算。 關鍵點是,
Presenter去更新View。但是Presenter還是需要調用View的方法,也就是說Presenter對View有依賴,這樣Presenter就沒辦法單獨做單元測試,非得等到界面做好以後才行。
於是上帝又做了一點改進,讓View層提取出接口,Presenter只依賴這個接口。
這樣Presenter不用依賴真正的界面就可以測試了,並且也增加了複用性,只要View實現了那個接口,Presenter就可以大發神威。
MVVM
使用了一段時間MVP以後,永不滿足的人類又覺得不爽了, 因為讓Presenter調用View的方法去設置界面,仍然需要大量的、煩人的代碼,這實在是一件不舒服的事情。
人類突發奇想: 能不能告訴View一個數據結構,然後View就能根據這個數據結構的變化而自動隨之變化呢?
上帝看到人類思考了,表示了讚賞。
他說,我來送你們一個叫做ViewModel的東西,它可以和View層綁定。 ViewModel的變化,View立刻就會變化。
人類問: ViewModel? 裡邊有什麼東西?
上帝說: 拿你們的薪水計算為例, ViewModel 差不多這樣:
當用戶在界面上點擊“計算”按鈕的時候, 你們需要設置一個SalaryViewModel中的標誌位:
salaryViewModel.isCalculating = true;
這樣View 中就可以自動給用戶展示一個消息:“正在計算....”
當薪水計算完成的時候, 如果沒有錯誤,SalaryViewModel 中grossSalary, netSalary,tax等屬性就有了值。 與此同時View 中對應的內容也會更新, 不用你們手工去設置, 很方便吧?
如果計算過程出錯, SalaryViewModel 的errMsg 會保存出錯消息, 同樣,View中會自動把這個錯誤消息給顯示出來, 很智能吧?
人類說:“怎麼可能這麼智能呢? 這裡的ViewModel 好像和View沒有什麼關係啊? 到底該怎麼綁定啊?!!!”
上帝笑了: 你們可以開發一個框架嘛? 讓兩者綁定起來不就行了?
人類沒有辦法,只好自己動手。
(注:實際上微軟的WPF和Silverlight, Android等框架和系統都可以實現View和ViewModel之間的映射和綁定)
Web應用程序的MVC
時間過得飛快,人類發明了互聯網,Web應用程序如雨後春筍般崛起,B/S(瀏覽器-服務器)開始大行其道。
用戶通過瀏覽器發出GET,POST請求,服務器端進行處理,處理完以後生成HTML給瀏覽器。
無論什麼操作,都是對服務器端URL的訪問。
人類突然發現,整個編程模型發生了鉅變, 不能簡單地套用原來的MVC和MVP了。
如果把HTML頁面比作原來桌面應用程序的View, 服務器無論是Controller還是Model都是無法遠程遙控這個View進行處理的。
人類這一次沒有去請教上帝,自己嘗試對MVC進行改良,其中有個叫Rod Johnson 帶領一幫人搞出的SpringMVC很成功。
不像桌面應用的MVC, 這裡的Model沒法給View 發通知。
也不像MVP, 這裡的Controller 也不會調用View的方法來設置界面。
實際上Controller 會選擇一個View, 然後把模型數據“丟過去”渲染。
原來的View 變成了 View Template(例如JSP , Velocity等等), 經過渲染後變成HTML發給瀏覽器展示給用戶。
有人把這種MVC稱為 基於Web的 MVC,以便和之前的MVC區別開來。
前後端的分離
人類早期的B/S應用程序中, 每次訪問服務器端, HTML就會整體發給瀏覽器,即所謂的整體刷新。
後來人類發明了AJAX, 可以做到局部刷新。
於是瀏覽器端的應用變得越來越複雜,再後來人類竟然發明了Web上的SPA(單頁應用程序),用起來的體驗和最初的桌面應用程序越來越像。
人類發現,那些MVC, MVVM之類的模式完全可以用到瀏覽器端嘛!
例如在瀏覽器端使用MVVM , 在服務器端可以使用MVC, 兩者結合起來:
前端和後端成功地分家了 !
原諒鏈接:https://mp.weixin.qq.com/s/EzxfJLb5Hjxyw0_S5rThvg?
閱讀更多 IT知識課堂 的文章