架構模式:MVC與MVVM

本文探討以下幾個問題:html

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

MVC與MVVM

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

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

MVC和MVVM都是架構模式!node

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

能夠看出MVC和MVVM都是爲了解耦「界面」和「業務邏輯」,只是解決方案不一樣!也能夠說MVVM是MVC的一種變體(ViewModel+Binder能夠說是Controller的一種實現方式),或者衍生!程序員

咱們先說,爲何要解耦「界面」和「業務邏輯」?web

早期的界面系統開發時,展現邏輯與數據是糅合在一塊兒的!以早期的Java爲例,全部的展現邏輯和業務邏輯都寫在了Servlet/JSP中,致使了:算法

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

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

  • 控制器(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;

這樣的拆分,提升了系統的:session

  • 可擴展性: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

參考資料

相關文章
相關標籤/搜索