探索MVC、MVP、MVVM和DDD架构在不同编程语言中的实现差异

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 分层架构特点

特性MVCDDD
主要目标分离 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

功能:点击按钮增减数值并更新视图。

  1. 模型层:CounterModel 类封装数据和操作逻辑,包含数值和标题的修改方法。
  2. 视图层:CounterView 类负责渲染界面,绑定模型数据,根据模型状态更新视图。
  3. 控制层: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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/74133.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

一文读懂 UML:基础概念与体系框架

UML 图是一种标准化的建模语言&#xff0c;在软件开发和系统设计等领域有着广泛的应用。以下是对 UML 图各类图的详细介绍&#xff1a; 1.用例图 定义&#xff1a;用例图是从用户角度描述系统功能的模型图&#xff0c;展现了系统的参与者与用例之间的关系。作用&#xff1a;帮…

Spring 及 Spring Boot 条件化注解(15个)完整列表及示例

Spring 及 Spring Boot 条件化注解完整列表及示例 1. 所有条件化注解列表 Spring 和 Spring Boot 提供了以下条件化注解&#xff08;共 15 个&#xff09;&#xff0c;用于在配置类或方法上实现条件化注册 Bean 或配置&#xff1a; 注解名称作用来源框架Conditional自定义条件…

【Kafka】深入探讨 Kafka 如何保证一致性

文章目录 Kafka 基本概念回顾​副本角色​ 数据写入一致性​同步副本&#xff08;ISR&#xff09;集合​数据读取一致性​故障处理与一致性恢复​总结​ 在分布式系统领域&#xff0c;数据一致性是至关重要的一环。作为一款高性能的分布式消息队列系统&#xff0c;Kafka 在设计…

从入门到精通:SQL注入防御与攻防实战——红队如何突破,蓝队如何应对!

引言&#xff1a;为什么SQL注入攻击依然如此强大&#xff1f; SQL注入&#xff08;SQL Injection&#xff09;是最古老且最常见的Web应用漏洞之一。尽管很多公司和组织都已经采取了WAF、防火墙、数据库隔离等防护措施&#xff0c;但SQL注入依然在许多情况下能够突破防线&#…

【算法day27】有效的数独——请你判断一个 9 x 9 的数独是否有效。只需要 根据以下规则 ,验证已经填入的数字是否有效即可。

36. 有效的数独 请你判断一个 9 x 9 的数独是否有效。只需要 根据以下规则 &#xff0c;验证已经填入的数字是否有效即可。 数字 1-9 在每一行只能出现一次。 数字 1-9 在每一列只能出现一次。 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。&#xff08;请参考示例…

leetcode 2360. 图中的最长环 困难

给你一个 n 个节点的 有向图 &#xff0c;节点编号为 0 到 n - 1 &#xff0c;其中每个节点 至多 有一条出边。 图用一个大小为 n 下标从 0 开始的数组 edges 表示&#xff0c;节点 i 到节点 edges[i] 之间有一条有向边。如果节点 i 没有出边&#xff0c;那么 edges[i] -1 。…

PySpur: AI 智能体可视化开发平台

GitHub&#xff1a;https://github.com/PySpur-Dev/pyspur 更多AI开源软件&#xff1a;发现分享好用的AI工具、AI开源软件、AI模型、AI变现 - 小众AI PySpur是一个开源的轻量级可视化AI智能体工作流构建器&#xff0c;旨在简化AI系统的开发流程。通过拖拽式界面&#xff0c;用户…

vcpkg安装及使用教程,以安装matio库解析mat文件为例

vcpkg安装及使用教程,以安装matio库解析mat文件为例 1. vcpkg安装2 安装matio三方库3 将三方库集成到VS中3.1 全局集成3.2 集成到特定工程4 结语Vcpkg 是微软开发的一款开源的 C/C++ 包管理工具,旨在简化 C/C++ 项目依赖库的安装和管理。它支持跨平台(Windows、Linux、macO…

LLM架构解析:NLP基础(第一部分)—— 模型、核心技术与发展历程全解析

本专栏深入探究从循环神经网络&#xff08;RNN&#xff09;到Transformer等自然语言处理&#xff08;NLP&#xff09;模型的架构&#xff0c;以及基于这些模型构建的应用程序。 本系列文章内容&#xff1a; NLP自然语言处理基础&#xff08;本文&#xff09;词嵌入&#xff0…

【Rtklib入门指南】2. 使用RTKLIB GUI进行观测数据分析

数据准备 下载2025年1月1日的香港CORS站数据和观测星历&#xff0c;详情参照如下博客&#xff1a; 使用GAMP_GOOD进行hk数据下载教程-CSDN博客 分析工具 RTKLIB 2.4.3 demo5&#xff08;也可以选用RTKLIB2.4.2&#xff0c;但不建议使用RTKLIB2.4.3&#xff09; 分析流程 …

suse15 sp1使用华为云软件源yum源zypper源

登录suse15终端&#xff0c; cd /etc/zypp/repos.d/进入目录后执行以下命令&#xff1a; zypper ar -fcg https://mirrors.huaweicloud.com/opensuse/distribution/leap/15.1/repo/oss HuaWeiCloud:15.1:OSS zypper ar -fcg https://mirrors.huaweicloud.com/opensuse/distribu…

首屏加载时间优化解决

&#x1f916; 作者简介&#xff1a;水煮白菜王&#xff08;juejin/csdn同名&#xff09; &#xff0c;一位前端劝退师 &#x1f47b; &#x1f440; 文章专栏&#xff1a; 高德AMap专栏 &#xff0c;记录一下平时学习在博客写作中记录&#xff0c;总结出的一些开发技巧✍。 感…

Sentinel[超详细讲解]-1

定义一系列 规则 &#x1f47a;&#xff0c;对资源进行 保护 &#x1f47a;&#xff0c; 如果违反的了规则&#xff0c;则抛出异常&#xff0c;看是否有fallback兜底处理&#xff0c;如果没有则直接返回异常信息&#x1f60e; 1. 快速入门 1.1 引入 Sentinel 依赖 <depend…

02-Docker 使用

docker:快速构建、运行、管理应用的工具,可以帮助我们下载应用镜像,创建并运行镜像的容器,从而快速部署应用 1、部署mysql 先停掉虚拟机中的MySQL,确保你的虚拟机已经安装Docker,且网络开通的情况下,执行下面命令即可安装MySQL(注意:若服务器上已经有mysql 占用了330…

@DeclareParents 注解实现接口功能增强:Spring中通过接口引入实现功能增强的完整示例

以下是Spring中通过接口引入实现功能增强的完整示例&#xff1a; // 1. 目标接口及实现类 package com.example;public interface Service {void doSomething(); }Component class ServiceImp implements Service {Overridepublic void doSomething() {System.out.println(&qu…

HTML中数字和字母不换行显示

HTML中数字和字母不换行显示的默认行为及如何通过CSS的word-wrap和word-break属性进行调整。 在HTML中标签中的数字和字母默认是不换行的&#xff0c;如果要将他们换行&#xff0c;在CSS中添加”word-wrap: break-word;” 即可解决 语法&#xff1a;word-wrap: normal|break-w…

Git团队开发命令总结

简易Git工作流 myname: 团队成员个人分支dev: 团队公共分支 个人独立分支开发 同步最新的【dev公共分支】到本地。【重要】基于最新的【dev公共分支】&#xff0c;创建【个人功能开发分支】。在此基础上开发。【个人功能开发分支】开发完成&#xff0c;推送到远程库。如果【…

Python人工智能大模型入门教程:从零构建高性能预测模型

引言&#xff1a;AI大模型时代的技术革命 在AlphaGo战胜人类棋手的里程碑事件后&#xff0c;人工智能技术进入爆发式发展阶段。本教程将带您从零开始&#xff0c;使用Python构建一个工业级神经网络模型。通过本教程&#xff0c;您不仅能掌握GPU加速训练、混合精度计算等前沿技…

python-leetcode 61.N皇后

题目&#xff1a; 按照国际象棋的规则&#xff0c;皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。 n 皇后问题 研究的是如何将 n 个皇后放置在 nn 的棋盘上&#xff0c;并且使皇后彼此之间不能相互攻击 给你一个整数 n &#xff0c;返回所有不同的 n 皇后问题 的解…

Mybatis_Plus中的常用注解

目录 1、TableName TableId TableId的type属性 TableField 1、TableName 经过以上的测试&#xff0c;在使用MyBatis-Plus实现基本的CRUD时&#xff0c;我们并没有指定要操作的表&#xff0c;只是在 Mapper接口继承BaseMapper时&#xff0c;设置了泛型User&#xff0c;而操…