1. 程式人生 > >架構模式:MVC與MVVM

架構模式:MVC與MVVM

本文探討如下幾個問題:

  • 什麼是MVC
  • 什麼是MVVM
  • MVC與MVVM對架構屬性的影響
  • MVC例項SpringMVC
  • MVVM例項Vue
  • MVC、MVVM與Layer中的Model,Controller有什麼區別?

MVC與MVVM

在「什麼是架構模式和架構風格」一文中,對架構模式的定義是:

Architecture Pattern: { Problem, Context } → architecture approach;
架構模式描述了一組元件之間的關係,用以解決特定上下文內的某個常見的架構問題

MVC和MVVM都是架構模式!

  • MVC描述了「Model,View,Controller」三者之間的關係,用以解決「有展示介面的系統」「介面開發與業務邏輯的耦合問題」
  • MVVM描述了[Model,View,ViewModel(和Binder)」三者之間的關係,用以解決「有展示介面的系統」「介面開發與業務邏輯的耦合問題」

可以看出MVC和MVVM都是為了解耦「介面」和「業務邏輯」,只是解決方案不同!也可以說MVVM是MVC的一種變體(ViewModel+Binder可以說是Controller的一種實現方式),或者衍生!

我們先說,為什麼要解耦「介面」和「業務邏輯」?

早期的介面系統開發時,展示邏輯與資料是糅合在一起的!以早期的Java為例,所有的展示邏輯和業務邏輯都寫在了Servlet/JSP中,導致了:

  • 單個檔案的程式碼量較多,既包含了展示程式碼又包含了業務邏輯
  • 違背了職責單一性原則
  • 修改麻煩
  • 複用性差
  • 不易於維護
  • ......

MVC將系統拆分為控制器、檢視和模型來解決上面的問題:

  • 控制器(Controller)- 負責轉發請求,對請求進行處理。
  • 檢視(View) - 介面設計人員進行圖形介面設計。
  • 模型(Model) - 程式設計師編寫程式應有的功能(實現演算法等等)、資料庫專家進行資料管理和資料庫設計(可以實現具體的功能)。

Wiki給出了一個Js模擬的MVC模式程式碼,能更清晰的理解三者之間的關係:

/** 模擬 Model, View, Controller */
var M = {}, V = {}, C = {};

/** Model 負責存放資料 */
M.data = "hello world";

/** View 負責將資料展示出來 */ V.render = (M) => { alert(M.data); } /** Controller 作為M和V的橋樑 */ C.handleOnload = () => { V.render(M); } /** 在頁面載入的時候呼叫Controller*/ window.onload = C.handleOnload;

這樣的拆分,提高了系統的:

  • 可擴充套件性:View、Model和Controller分別負責檢視、資料和控制,可獨立進化。能較方便的增加新功能。
  • 可維護性:結構清晰,多個View可以複用一個Model,減少了程式碼重複
  • 可測試性:每個元件的職責單一,可以對Model進行自動化測試
  • 靈活性:Controller 可以用來連線不同的 Model 和 View 去完成使用者的需求
  • 可配置性:給定一些可重用的 Model 、 View 和Controller 可以根據使用者的需求選擇適當的 Model 進行處理,然後選擇適當的的 View 來處理結果顯示給使用者

而MVVM模式將系統拆分為檢視、模型和檢視模型以及繫結器來解決耦合問題:

  • Model:Model是指代表真實狀態內容的領域模型(面向物件),或指代表內容的資料訪問層(以資料為中心)。和MVC中的Model指代的內容相似。
  • View:就像在MVC中一樣,View是使用者在螢幕上看到的結構、佈局和外觀(UI)。
  • ViewModel:ViewModel是暴露公共屬性和命令的檢視的抽象。
  • Binder:繫結器是MVVM模式裡的一個隱含元件,ViewModel中聲明瞭資料與View之間的繫結關係,而繫結器來處理宣告的繫結關係。

ViewModel和Binder的關係就像Java裡的註解與註解處理器之間的關係:註解只是標示了某個欄位、方法或某個類應該具備什麼屬性;註解處理器根據註解,對被註解的欄位、方法或某個類來進行實際的處理。

這樣的拆分,與MVC模式一樣,提高了相同的系統屬性,只是方式有一些差異:

  • 可擴充套件性:View、Model和ViewModel(和Binder)分別負責檢視、資料和資料檢視繫結,可獨立進化
  • 可維護性:結構清晰,多個View可以複用一個Model,減少了程式碼重複
  • 可測試性:每個元件的職責單一,可以對Model進行自動化測試。前臺也可以對ViewModel進行自動化測試。
  • 靈活性:ViewModel 可以用來繫結不同的 Model 和 View 去完成使用者的需求
  • 可配置性:給定一些可重用的 Model 、 View可以根據使用者的需求選擇適當的 Model 進行處理,然後選擇適當的的 View 來處理結果顯示給使用者

SpringMVC

SpringMVC是目前使用比較廣泛的MVC框架!它是MVC與「前端控制器」的混合體!

前端控制器模式(Front Controller Pattern)是用來提供一個集中的請求處理機制,所有的請求都將由一個單一的處理程式處理。該處理程式可以做認證/授權/記錄日誌,或者跟蹤請求,然後把請求傳給相應的處理程式。前端控制器包含如下元件:

前端控制器(Front Controller):處理應用程式所有型別請求的單個處理程式,應用程式可以是基於 web 的應用程式,也可以是基於桌面的應用程式。
排程器(Dispatcher):前端控制器可能使用一個排程器物件來排程請求到相應的具體處理程式。
檢視(View):檢視是為請求而建立的物件。

在SpringMVC中:

  • View:指的是各種展現模板,比如:velocity,freemark,theamleaf等
  • Controller:指的是前端控制器與Controller層
  • Model:指的是Controller後的後續處理元件,比如Service,Model,Domain,DAO層等

SpringMVC的前端控制器做了很多事情,結構如下圖:

  • HandlerMappingMap: 維護了請求與處理程式(一組前置攔截器+處理程式+後置攔截器)之間的關係,如何維護對映關係,由具體的實現決定。例如:RequestMappingHandlerMapping基於@RequestMapping註解來維護對映關係;SimpleUrlHandlerMapping根據URI匹配關係來處理對映關係
  • HandlerAdapter:用於執行具體的處理程式。由於不同的對映關係,執行處理程式的方式也不一樣,HandlerAdapter封裝了這些執行差異
  • HandlerException:異常處理程式,將異常對映到處理程式、或HTML或其它元件上。即可以通過配置,統一的處理某一種型別的異常
  • ViewResolver:通過解析「基於字串的檢視名稱」來找到真實的檢視,來進行渲染,回寫到響應中
  • LocaleResolver, LocaleContextResolver:處理國際化
  • ThemeResolver:處理主題
  • MultipartResolver:處理multi-part請求 (上傳檔案)
  • FlashMapManager:實現Flash作用域。因為標準J2EE中只提供了page,request,session,application四種作用域。Spring通過FlashMapManager實現了Flash作用域,用於在單個請求內傳遞引數。

Vue

Vue實現的是MVVM模式!

在Vue中:

  • View:指的是各種template,即基於html語法的展示
  • ViewModel:指的是對應的js,宣告繫結的元素及繫結的資料
  • Binder:處理template與js的繫結邏輯
  • Model:指的是獲取資料的邏輯。如果使用的是node,則就是node相關邏輯程式碼;如果呼叫的是遠端服務,則指的是遠端服務

一個簡單的Vue程式碼如下:

<!DOCTYPE html>
<html lang=en> <head> <meta charset="utf-8"/> <title>Hello world</title> <script src="vue.js"></script> </head> <body> <div id="app">{{content}}</div> <script> var app = new Vue({ el:'#app', //vue例項處理哪個dom data:{ //定義 content:'hello world' }, methods:{ fetchData: function() { // 獲取資料 } } }); </script> </body> </html>

下圖是Vue的生命週期:

  • Vue物件就是ViewModel,它聲明瞭:

    • 要處理哪個dom「el」
    • 有哪些資料「data」
    • 有哪些方法「methods」
    • 生命週期鉤子(beforeCreate,created...)
  • Binder(Vue框架處理程式碼)根據上面的宣告,來進行相應的處理

此Model非彼Model

在上面有三處提到了Model和Controller,這三處的Model和Controller並不完全相同!

三處Model:

  • MVC中的Model:這裡的Mode指的是提供資料相關服務的元件。或者說是業務處理邏輯。
  • MVVM中的Model:和MVC一致
  • 系統中的Model:指的是分層系統中的Model層(程式碼分層),主要作為系統與持久層的資料載體,可以是隻有get、set方法的POJO也可以是包含領域方法的領域物件

三處Controller:

  • MVC中的Controller:指的是連線View和Model的元件
  • Spring中的前端控制器:Controller的一種實現,統一處理請求
  • Spring中的Controller:分層系統裡的Controller層(程式碼分層,可能叫Handler更合適),屬於MVC中的Controller

參考資料