歡迎光臨
每天分享高質量文章

你真的理解了MVC, MVP, MVVM嗎?

來自:碼農翻身(微訊號:coderising)

前言: 準備寫這篇文章的時候 , 我自認為對MVC已經有深刻理解了,可是畫圖的時候發現,理解還是有漏洞,於是又閱讀,思考,整理,加深了理解, 寫了這篇文章, 估計還有漏洞,歡迎討論。

這再一次說明瞭寫作的好處: 很多時候自以為理解了,實際上腦海中有很多想當然的假設,寫作會把這些假設給暴露出來。

大概是二三十年前, 人類逐漸從命令列介面時代走出來,進化到了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傳遞過來,不就可以展示資料了嗎?

// View的方法,被Model呼叫: 
public void onResult(Salary salary){  
     //把個人所得稅(salary.getTax()) 展示到一個文字框
     //把凈收入(salary.getNetPay()) 展示到另外一個文字框
     ......
}

畫成流程圖的話是這個樣子:

大家都覺得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呼叫
public void calculateSalary(){
   //呼叫Model計算薪水
   view.showTax(xxx);    // 呼叫View顯示所得稅
   view.showNetPay(xxxx);//  呼叫View凈收入
   ......
}

但是Presenter還是需要呼叫View的方法,也就是說Presenter對View有依賴,這樣Presenter就沒辦法單獨做單元測試,非得等到介面做好以後才行。

於是上帝又做了一點改進,讓View層提取出介面,Presenter只依賴這個介面

這樣Presenter不用依賴真正的介面就可以測試了,並且也增加了復用性,只要View實現了那個介面,Presenter就可以大發神威。

MVVM

使用了一段時間MVP以後,永不滿足的人類又覺得不爽了, 因為讓Presenter呼叫View的方法去設定介面,仍然需要大量的、煩人的程式碼,這實在是一件不舒服的事情。

人類突發奇想: 能不能告訴View一個資料結構,然後View就能根據這個資料結構的變化而自動隨之變化呢?

上帝看到人類思考了,表示了贊賞。

他說,我來送你們一個叫做ViewModel的東西,它可以和View層系結。 ViewModel的變化,View立刻就會變化。

人類問: ViewModel? 裡邊有什麼東西?

上帝說: 拿你們的薪水計算為例, ViewModel 差不多這樣:

public class SalaryViewModel{
   String grossSalary;  //稅前收入,和View中的相關欄位對應
   String netSalary;    //凈收入,和View中的相關欄位對應
   String tax;          //個人所得稅,和View中的相關欄位對應
   ......
   boolean  isCalculating;  // 一個標誌位,表示正在計算
   String errMsg;           // 如果出錯的話,記錄出錯訊息。
}

當使用者在介面上點選“計算”按鈕的時候, 你們需要設定一個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, 兩者結合起來:

前端和後端成功地分家了 !


●編號382,輸入編號直達本文

●輸入m獲取文章目錄

推薦↓↓↓

 

演演算法與資料結構

更多推薦18個技術類微信公眾號

涵蓋:程式人生、演演算法與資料結構、駭客技術與網路安全、大資料技術、前端開發、Java、Python、Web開發、安卓開發、iOS開發、C/C++、.NET、Linux、資料庫、運維等。

贊(0)

分享創造快樂