MVC與MVP/MVVM/DDD架構對比,不同語言實現
MVC 分層架構設計概述
模型-視圖-控制器(Model-View-Controller,簡稱 MVC)是一種經典軟件架構設計,通過分層解耦,使得系統結構清晰和易于維護,具有良好的可擴展性。MVC 適用于需要清晰分離用戶界面、業務邏輯和數據管理的應用場景。隨著MVC的發展,還衍生出了MVP、MVVM以及領域驅動設計(DDD)等架構,這些都是為了讓龐大的系統變得簡單,易于人們理解。
MVC 結構圖形示例
以Web后端開發為例
用戶請求 | v
+---------+ +-----------+ +-----------+
| View | <--- | Controller| ---> | Model |
+---------+ +-----------+ +-----------+^ v| Model數據映射到View |****--------------------------------****
MVC各層職責
- 視圖層(View):處理用戶界面展示和用戶輸入事件
- 控制器層(Controller):接收用戶請求,協調模型和視圖
- 模型層(Model):封裝業務邏輯和數據結構
MVC是一種軟件分層設計模式,目的是為了使得各層級解耦,使得代碼更清晰和易于維護。常用來跟MVP、MVVM以及DDD分層架構對比,以下對這幾種架構進行詳細分析。MVC不同語言源碼實現:https://github.com/microwind/design-patterns/tree/main/mvx/mvc
MVC 分層架構與 DDD 分層架構對比
MVC 以界面與數據分離為核心目標,強調快速開發;DDD 以領域模型驅動為核心思想,專注于復雜業務系統的可持續架構設計。
DDD 結構圖形示例
+--------------------+
| 用戶界面層 |
| User Interface |
| 含Controller/UI |
+--------------------+|v
+--------------------+
| 應用服務層 |
| Application Layer |
| 含Service/DTO |
+--------------------+|v
+--------------------+
| 領域層 |
| Domain Layer |
| 含Model/Service |
+--------------------+|v
+----------------------+
| 基礎設施層 |
| Infrastructure Layer |
| 含Repository/Message |
+----------------------+
DDD各語言源碼:https://github.com/microwind/design-patterns/tree/main/domain-driven-design
MVC 分層架構與 DDD 分層架構特點
特性 | MVC | DDD |
---|---|---|
主要目標 | 分離 UI、邏輯和數據 | 解決復雜領域建模問題 |
核心分層 | 3 層(View、Controller、Model) | 4 層(UI、應用、領域、基礎設施) |
適用場景 | Web 應用、前端交互密集型系統 | 企業級復雜業務系統(如金融交易、供應鏈管理) |
開發效率 | 快速原型開發,中小型項目友好 | 需前期領域建模,適合長期演進的大型項目 |
MVC與MVP、MVVM的分層架構對比
MVC與MVP總體上一致,只是在View與Model是否完全解耦上有差別。MVP通過接口隔離實現完全解耦,而MVC允許視圖直接訪問模型。MVC與MVVM的本質差異在于數據同步機制:MVVM通過雙向綁定實現自動數據同步,MVC則依賴手動進行狀態管理。
MVP(Model-View-Presenter)結構圖形
User Input | v 由主持人代理View和Model交互
+---------+ +-----------+ +-----------+
| View | <--> | Presenter | <---> | Model |
+---------+ +-----------+ +-----------+1. MVP 主要用于前端開發,尤其是界面渲染,當一個界面需要針對多個視圖數據進行渲染時,采用MVP比MVC更合適。
2. MVP 下 View 與 Model 隔離,View 中沒有對應 Model 概念,數據由 Presenter 代為傳遞。
MVP各語言源碼:https://github.com/microwind/design-patterns/tree/main/mvx/mvp
MVVM(Model-View-ViewModel)
User Input |v 將View與Model雙向數據綁定
+---------+ +-----------+ +-----------+
| View | ---> | ViewModel | <--> | Model |
+---------+ +-----------+ +-----------+^ || vData Binding(由Agent監聽數據變化)1. MVVM 從 View 觸發,監聽事件執行數據更新。
2. 通過代理監聽數據變化,自動更新視圖。
MVVM各語言源碼:https://github.com/microwind/design-patterns/tree/main/mvx/mvvm
MVC與MVP、MVVM的分層架構特點
模式 | 控制流程描述 | View與Model耦合度 | 組件角色 |
---|---|---|---|
MVC | 請求驅動模式: Controller 接收 View 請求 → 操作 Model → Model 直接通知 View 更新;View 主動監聽 Model 事件。 | 存在一定耦合,View 直接綁定 Model。 | Controller 處理邏輯;View 展示數據;Model 管理數據。 |
MVP | 中介者模式:View 與 Presenter 雙向交互:用戶操作觸發事件 → Presenter 調用 Model 更新 → Presenter 通知 View 更新。 | 完全解耦,View 僅與 Presenter 交互,Model 不直接通知 View。 | Presenter 充當中介者;View 僅負責展示;Model 管理數據。 |
MVVM | 響應式編程模式:利用數據綁定:View 與 ViewModel 雙向綁定,ViewModel 操作 Model 后自動反映在 View 上。 | 完全解耦,借助數據綁定技術實現 View 與 Model 之間的間接通信。 | ViewModel 充當橋梁;View 為聲明式UI層;Model 純數據結構。 |
MVC是分層架構思想的先驅,后來MVP、MVVM、DDD等才流行開來,可以對比下幾種分層代碼,理解其中的變遷:https://github.com/microwind/design-patterns/tree/main/mvx
MVC 的應用場景
- Web 應用程序(如電商網站、博客系統)
- 前后端分離項目(RESTful API + 前端框架)
- 桌面 GUI 應用(Java Swing、C# WinForms)
- 移動端應用(Android Activity 結構)
MVC 的例子(C、Java、JavaScript、Go、Python等)
MVC最早從Smalltalk語言發展而來,后來經過Java、C++、.NET等將其發揚光大,除了傳統的面向對象語言可以實現MVC模式,其他各種高級語言都可以實現MVC。需要注意的是MVC并非一種技術,而是一種理念。只要秉持這種分層思想,那么任何語言都可以實現MVC思想。
C 語言實現 MVC
/* 視圖層(View)*/
// view.c
#include <stdio.h>
#include "controller.h"void display_order(Order order) {printf("Order ID: %s\nCustomer: %s\nAmount: %.2f\n",order.id, order.customer_name, order.amount);
}/* 控制器層(Controller)*/
// controller.c
#include "controller.h"
#include "repository.h"void create_order(Order order) {save_order(order);
}Order get_order(char* id) {return find_order(id);
}/* 模型層(Model)*/
// order.h
typedef struct {char id[10];char customer_name[50];float amount;
} Order;/* 數據訪問層(Repository)*/
// repository.c
#include <string.h>
#include "repository.h"static Order orders[100];
static int count = 0;void save_order(Order order) {orders[count++] = order;
}Order find_order(char* id) {for (int i = 0; i < count; i++) {if (strcmp(orders[i].id, id) == 0) {return orders[i];}}Order empty = { "", "", 0 };return empty;
}
Java 語言實現 MVC
/* 視圖層(View)*/
// Thymeleaf 模板 (orders.html)
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<body><h1>訂單列表</h1><ul><li th:each="order : ${orders}"><span th:text="${order.id}"></span> - <span th:text="${order.customerName}"></span></li></ul>
</body>
</html>/* 控制器層(Controller)*/
// OrderController.java
@Controller
@RequestMapping("/orders")
public class OrderController {private final OrderService service;@Autowiredpublic OrderController(OrderService service) {this.service = service;}@GetMappingpublic String listOrders(Model model) {model.addAttribute("orders", service.getAllOrders());return "orders";}
}/* 模型層(Model)*/
// Order.java
@Entity
public class Order {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;private String customerName;private BigDecimal amount;// Getters & Setters
}/* 數據訪問層(Repository)*/
// OrderRepository.java
public interface OrderRepository extends JpaRepository<Order, Long> {List<Order> findByCustomerName(String name);
}
Go 語言實現 MVC
/* 視圖層(View)*/
// view.go
func RenderOrder(w http.ResponseWriter, order Order) {fmt.Fprintf(w, "ID: %s\nCustomer: %s\nAmount: %.2f",order.ID, order.CustomerName, order.Amount)
}/* 控制器層(Controller)*/
// controller.go
func OrderHandler(w http.ResponseWriter, r *http.Request) {id := r.URL.Query().Get("id")order := repository.GetOrder(id)RenderOrder(w, order)
}/* 模型層(Model)*/
// order.go
type Order struct {ID stringCustomerName stringAmount float64
}/* 數據訪問層(Repository)*/
// repository.go
var orders = make(map[string]Order)func GetOrder(id string) Order {return orders[id]
}func SaveOrder(order Order) {orders[order.ID] = order
}
Python 語言實現 MVC(Flask)
# 視圖層(View)
# templates/orders.html
<html>
<body><h1>Orders</h1><ul>{% for order in orders %}<li>{{ order.id }} - {{ order.customer_name }}</li>{% endfor %}</ul>
</body>
</html># 控制器層(Controller)
# app.py
from flask import Flask, render_template
from service import OrderServiceapp = Flask(__name__)
service = OrderService()@app.route('/orders')
def list_orders():orders = service.get_all_orders()return render_template('orders.html', orders=orders)# 模型層(Model)
# order.py
class Order:def __init__(self, id, customer_name, amount):self.id = idself.customer_name = customer_nameself.amount = amount# 數據訪問層(Repository)
# repository.py
class OrderRepository:def __init__(self):self.orders = {}def save(self, order):self.orders[order.id] = orderdef get_all(self):return list(self.orders.values())
JavaScript 實現 MVC(Express.js)
/* 視圖層(View)*/
// views/orders.ejs
<!DOCTYPE html>
<html>
<body><h1>Orders</h1><ul><% orders.forEach(order => { %><li><%= order.id %> - <%= order.customerName %></li><% }) %></ul>
</body>
</html>/* 控制器層(Controller)*/
// routes/orderRoutes.js
const express = require('express');
const router = express.Router();
const service = require('../services/orderService');router.get('/orders', async (req, res) => {const orders = await service.getAllOrders();res.render('orders', { orders });
});/* 模型層(Model)*/
// models/Order.js
class Order {constructor(id, customerName, amount) {this.id = id;this.customerName = customerName;this.amount = amount;}
}/* 數據訪問層(Repository)*/
// repositories/orderRepository.js
class OrderRepository {constructor() {this.db = new Map();}save(order) {this.db.set(order.id, order);}getAll() {return Array.from(this.db.values());}
}
JavaScript 前端版 MVC
功能:點擊按鈕增減數值并更新視圖。
- 模型層:CounterModel 類封裝數據和操作邏輯,包含數值和標題的修改方法。
- 視圖層:CounterView 類負責渲染界面,綁定模型數據,根據模型狀態更新視圖。
- 控制層:CounterController 類作為中間層,綁定視圖和模型,監聽事件,實現數據和視圖的更新。
// Model 類:封裝數據邏輯
class CounterModel {constructor() {// 初始化數據this.title = '點擊更換標題';this.num = 0;}// 標題操作:增加標題changeTitle() {this.title = '點擊更換標題' + Math.floor(Math.random() * 100);}// 數據操作方法:增加數值increment() {this.num++;}// 數據操作方法:減少數值decrement() {this.num--;}
}// View 類:處理界面渲染
class CounterView {template(data = {}) {return `<div class="counter"><h3 class="title">${data.title}</h3><button class="dec-btn">-</button><span class="num">${data.num}</span><button class="inc-btn">+</button></div>`;}constructor(model, container) {this.model = model; // 綁定模型,這是跟MVP最大區別this.$container = container;this.init();}// 初始化DOMinit() {this.$container.innerHTML = this.template(this.model);this.$titleEl = this.$container.querySelector('.title');this.$numEl = this.$container.querySelector('.num');this.$incBtn = this.$container.querySelector('.inc-btn');this.$decBtn = this.$container.querySelector('.dec-btn');}// 更新視圖方法render() {// 可以根據數據是否有變化來確定要更新哪個字段const data = this.modelthis.$titleEl.textContent = data.title;this.$numEl.textContent = data.num;}
}// Controller 類:處理用戶輸入
class CounterController {constructor(model, view) {this.model = model;this.view = view;this.bindEvents();}// 綁定DOM事件bindEvents() {this.view.$titleEl.addEventListener('click', () => this.changeTitleHandle());this.view.$incBtn.addEventListener('click', () => this.incrementHandle());this.view.$decBtn.addEventListener('click', () => this.decrementHandle());}changeTitleHandle() {this.model.changeTitle();this.view.render(); // 直接更新視圖,不必傳遞model}// 事件處理:增加操作incrementHandle() {this.model.increment();this.view.render(); // 直接更新視圖,不必傳遞model}// 事件處理:減少操作decrementHandle() {this.model.decrement();this.view.render(); // 直接更新視圖,不必傳遞model}
}// 初始化應用
const appContainer = document.body;
const model = new CounterModel();
const view = new CounterView(model, appContainer);
const controller = new CounterController(model, view);
總結
- MVC 適用于快速開發 Web 應用,強調職責分離。
- MVP 適用于對視圖和業務邏輯分離要求較高的場景,強調展示層對視圖和模型的協調。
- MVVM ?適用于復雜視圖與模型交互的應用,利用數據綁定機制自動同步視圖和模型的狀態。
- DDD 適用于復雜業務系統,強調領域建模。
- 核心優勢:代碼可維護性強,團隊協作效率高。
- 選型建議:中小型項目優先考慮 MVC,復雜業務系統可結合 DDD。
最后
- 要用 MVC 嗎? 90% 的 Web 項目都適合 MVC 架構
- 如何判斷適用性? 如果需求變化主要集中在 UI 和流程的中小型項目,MVC 是最佳選擇
- 擴展建議:大型項目可在 MVC 基礎上增加 Service 層和 DTO 對象,或者直接采用DDD架構
- 更多設計與架構源碼: https://github.com/microwind/design-patterns