MVC、MVP、MVVM 三個縮寫總是一起出現,讓人以為它們是同一條演進線——MVC 過時了,MVP 改進了,MVVM 最新。這個理解不對。
它們解決的是不同環境下的不同問題。理解這個區別,比記住每個縮寫的定義更有用。
MVC:1979 年的 Smalltalk,解決 UI 和邏輯混在一起
MVC 的起點是 Trygve Reenskaug 在 Xerox PARC 為 Smalltalk-80 設計的 UI 框架,最早的問題是:UI 程式碼和業務邏輯混在一起,改 UI 就可能影響業務邏輯。
分成三個角色:
- Model:業務邏輯和資料,不知道 UI 的存在
- View:只負責呈現,監聽 Model 的變更
- Controller:接收用戶輸入,更新 Model
MVC 的核心貢獻是把 Model 和 View 分開,讓業務邏輯不受 UI 拖累。在桌面應用時代(Swing、.NET WinForms),這個分法很有效。
Web 框架(Rails、Spring MVC、Django)後來採用了 MVC 的名字,但改了它的語意——Web MVC 裡 Controller 接收 HTTP request,View 是 template,Model 是 ORM entity,和原始 Smalltalk MVC 已經是不同的東西。
MVP:解決 MVC 在桌面應用難以測試的問題
MVC 在桌面 UI 框架(如 .NET WinForms、Android View)有一個問題:View 直接和 Model 溝通,View 有時候包含業務邏輯,而 View 很難在測試環境裡隔離(需要 UI 框架支援)。
MVP(Martin Fowler 整理,2006)把 Controller 換成 Presenter,並改變了溝通路徑:
- View 只是被動介面——所有用戶事件都轉給 Presenter,不做任何邏輯決定
- Presenter 是「View 的腦子」——處理邏輯,告訴 View 要顯示什麼
- View 和 Model 不直接溝通,全部透過 Presenter 協調
這讓 Presenter 可以在不起 UI 的情況下單獨測試——Mock 一個 View 介面,直接測 Presenter 的邏輯。這在 Android 開發(pre-Jetpack)和 WinForms 時代非常流行。
MVVM:解決手動同步 View 和 Model 的問題
MVP 的問題是:Presenter 要手動把 Model 的資料更新到 View——view.setUsername(user.name)、view.setEmail(user.email)——每個欄位都要一行,Model 有 20 個欄位就要 20 行同步程式碼。
MVVM(Microsoft 為 WPF / Silverlight 提出)引入了雙向資料綁定:
- ViewModel 持有 UI 需要的資料,以「可觀察」的方式暴露出來
- View 直接綁定到 ViewModel 的屬性,ViewModel 更新,View 自動更新——不需要手動同步程式碼
這個模式在有框架支援雙向綁定的環境裡非常自然:WPF、Angular(雙向 [(ngModel)])、Vue(v-model)。
React 是哪種?
React 不是純粹的 MVC、MVP 或 MVVM,它更接近單向資料流的模型(靈感來自 Flux)——狀態從上層元件流向下層,用戶事件觸發 dispatch,更新狀態,再重新 render。
Vue 提供雙向綁定(更接近 MVVM),但本質上也是元件化框架,不是傳統 MVVM 的 ViewModel 對應關係。Angular 最接近 MVVM(有明確的 Component 類別當 ViewModel),但也有 Service 層承接業務邏輯。
現代前端框架基本上把「元件」作為基本單位,每個元件內部可以有自己的狀態管理邏輯——MVC / MVP / MVVM 是幫助你理解歷史和設計意圖的框架,不是現代前端開發的日常語言。
什麼時候這三個名字還有用
理解 MVC / MVP / MVVM 的場景:
閱讀舊文件或框架文檔:Android View-based 架構的文件大量用 MVP;Angular 官方文件用 MVVM 詞彙解釋 Component;Rails 用 MVC 描述路由和 controller 的關係。
架構討論:團隊在討論「UI 邏輯放哪」時,MVP 的 Presenter / MVVM 的 ViewModel 是有效的溝通詞彙,讓大家對「哪種邏輯應該在哪一層」有共識。
桌面 / 移動開發:WPF、SwiftUI、Jetpack Compose——這些環境的架構討論仍然大量使用這三個詞,MVVM 在這些領域是主流。
上一篇 → Onion Architecture
下一篇 → 架構模式全景