架構模式: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

參考資料


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM