Qt数据和视图分离——中MCV和MVVM
智能指针
- 一、背景知识
- 二、命令式编程 vs 声明式编程
- 2.1 命令式编程(Imperative Programming)
- 2.2 声明式编程(Declarative Programming)
- 三、 MVC(Model-View-Controller)
- 3.1 模型(Model)
- 3.2 视图(View)
- 3.3 控制器(Controller)
- 3.4 MVC 工作流程
- 3.5 总结
- 四、 MVVM(Model-View-ViewModel)
- 4.1 模型(Model)
- 4.2 视图(View)
- 4.3 视图模型(ViewModel)
- 4.5 MVVM 工作流程
- 4.6 总结
- 五、 MVC vs MVVM
- 5.1 MVC(模型-视图-控制器)
- 5.2 MVVM (模型-视图-视图模型)在Qt中的使用
- 5.3 区别总结
一、背景知识
回忆我们最初学Qt
的时候,经常通常都是在一个文件中,创建很多控件,然后在当前文件中处理该控件,包括信号槽的处理,控件的变化等等…我们最初用的就是所谓的命令式编程 也就是MVC
随着我们的项目愈发庞大,我们发现,我们的代码越来越难以维护,并且出现的bug
很难定位,俗称(屎山),后面我们了解到声明式编程 也就是MVVM
二、命令式编程 vs 声明式编程
2.1 命令式编程(Imperative Programming)
命令式编程是一种通过一系列指令和命令来改变程序状态的范式。开发者需要显式地描述如何进行某些操作,通常通过控制流(如条件语句、循环等),逐步指示计算机完成任务。这种方式强调“如何做”。
👁️👁️特点:
- 步骤驱动:开发者需要明确每个操作步骤。
- 状态管理:程序的状态在每一步变化中逐渐改变。
- 控制流:使用条件和循环来控制程序的执行顺序。
根据以下实例,我们通过信号和槽直接处理用户的点击事件,通过显式地调用方法来更新UI
。
#include <QApplication>
#include <QPushButton>
#include <QLabel>
#include <QVBoxLayout>
#include <QWidget> int main(int argc, char *argv[]) { QApplication a(argc, argv); QWidget window; QVBoxLayout layout(&window); QLabel label("Hello, World!"); QPushButton button("Change Text"); layout.addWidget(&label); layout.addWidget(&button); // 命令式风格 QObject::connect(&button, &QPushButton::clicked, [&]() { label.setText("Text Changed!"); }); window.show(); return a.exec();
}
2.2 声明式编程(Declarative Programming)
声明式编程是一种通过声明或表达程序所需的结果而不是详细说明实现过程的范式。在这种方式中,开发者告诉计算机“做什么”,而不必去说明“怎么做”。这通常通过描述数据结构、约束条件或使用高级抽象来实现,降低了复杂度。
👁️👁️特点:
- 结果驱动:开发者关注于程序的目标,而不是实现细节。
- 数据抽象:数据和逻辑的分离,通常使用模型来管理数据。
- 更高的可维护性:由于代码更简洁,结构更清晰,通常更易于维护和扩展。
参考以下代码,数据的管理与视图的显示分开,模型负责处理数据,视图只关心如何展示,而不需关心背后的逻辑。
#include <QApplication>
#include <QListView>
#include <QStringListModel>
#include <QVBoxLayout>
#include <QWidget>
#include <QPushButton> class MyModel : public QStringListModel {
public: MyModel(QObject *parent = nullptr) : QStringListModel(parent) { setStringList({"Item 1", "Item 2", "Item 3"}); } void addItem(const QString &item) { QStringList items = stringList(); items.append(item); setStringList(items); }
}; int main(int argc, char *argv[]) { QApplication a(argc, argv); QWidget window; QVBoxLayout layout(&window); MyModel model; QListView view; view.setModel(&model); QPushButton button("Add Item"); QObject::connect(&button, &QPushButton::clicked, [&]() { model.addItem("New Item"); }); layout.addWidget(&view); layout.addWidget(&button); window.show(); return a.exec();
}
在Qt中,常见的数据和视图分离方式有使用Model-View-Controller (MVC)、Model-View-ViewModel (MVVM) 等设计模式。这些方法允许更清晰的分离数据逻辑和用户界面,从而提高代码的可维护性和可扩展性。
三、 MVC(Model-View-Controller)
MVC(Model-View-Controller)是一种软件架构模式,用于实现用户界面和应用程序之间的分离。这种模式帮助组织代码,提高了应用程序的可维护性和可扩展性。MVC 将应用程序分为三个主要组件:模型(Model)、视图(View)和控制器(Controller)。
3.1 模型(Model)
模型负责处理应用程序的数据和业务逻辑。它直接与数据库交互,进行数据的存取和处理。在 MVC 模式中,模型独立于用户界面和控制逻辑,确保应用程序的数据状态保持一致。
👁️👁️功能:
- 管理应用程序的数据。
- 执行业务逻辑,包括数据验证和计算。
- 通知视图更新的数据状态(通常通过观察者模式)。
3.2 视图(View)
视图是用户界面部分,负责显示数据和呈现给用户。它从模型获取数据,并根据这些数据生成视觉输出。视图只是负责表现,不直接处理数据逻辑。
👁️👁️功能:
- 提供用户交互的界面。
- 显示模型中的数据。
- 监听用户输入,并将其转发给控制器。
3.3 控制器(Controller)
控制器是连接模型和视图的中介。它接收用户的输入,处理请求,并更新模型或视图。控制器会根据用户操作的类型选择相应的模型和视图进行处理。
👁️👁️功能:
- 接收用户输入和事件(如按钮点击)。
- 调用模型以获取或修改数据。
- 选择和更新适当的视图以呈现新的数据。
3.4 MVC 工作流程
- 用户与视图交互(例如,点击按钮)。
- 视图将用户的输入传递给控制器。
- 控制器处理输入,并可能请求模型更新(例如,保存用户修改的数据)。
- 模型更新后,会通知视图更新(通常通过观察者模式)。
- 视图接收到更新并重新渲染以显示新数据。
👁️👁️优点
- 分离关注点:每个组件的职责明确,代码更清晰。
- 可维护性高:分离使得修改任何一个部分对其他部分的影响减小。
- 可扩展性:容易添加新功能或修改现有功能;例如,可以独立于模型数据重构视图。
- 复用性:模型和视图可以在不同的应用程序中复用。
3.5 总结
MVC 是一种非常流行的设计模式,广泛用于Web应用程序和桌面应用程序开发。通过将用户界面与业务逻辑清晰分离,MVC 提供了一个灵活和高效的开发方式,有助于构建可维护和可扩展的应用程序。
MVVM(Model-View-ViewModel)是一种软件架构模式,主要用于构建用户界面,尤其在需要双向数据绑定的应用程序中非常常见。MVVM 模式通过分离关注点,提高了代码的可维护性和可测试性。它将应用程序分为三个主要组件:模型(Model)、视图(View)和视图模型(ViewModel)。
四、 MVVM(Model-View-ViewModel)
是一种软件架构模式,主要用于构建用户界面,尤其在需要双向数据绑定的应用程序中非常常见。MVVM 模式通过分离关注点,提高了代码的可维护性和可测试性。它将应用程序分为三个主要组件:模型(Model)、视图(View)和视图模型(ViewModel)。
4.1 模型(Model)
模型与 MVC 中的模型类似,负责处理应用程序的数据和业务逻辑。它可以是数据模型、数据存取层,或者业务逻辑代码。
👁️👁️功能:
- 表示应用程序的核心数据和业务逻辑。
- 直接与数据库和后端进行交互。
4.2 视图(View)
视图是用户界面部分,展示用户与应用程序交互的信息。视图通常包括按钮、文本框、图形等界面元素。
👁️👁️功能:
- 提供用户交互的界面。
- 直接绑定到视图模型,以反映数据的变化。
4.3 视图模型(ViewModel)
视图模型是连接视图和模型的中介,负责处理与视图的交互逻辑,并将数据提供给视图进行展示。视图模型使用数据绑定来更新视图,同时也提供处理用户输入和事件的逻辑。
👁️👁️功能:
- 提供视图所需的数据和命令。
- 通过属性通知视图更新(通常利用数据绑定机制)。
- 处理视图的状态和行为,与模型交互。
4.5 MVVM 工作流程
- 用户与视图交互(例如,点击按钮)。
- 视图通过数据绑定获得视图模型中的数据和命令。
- 视图模型接收到用户输入并更新模型或处理业务逻辑。
- 模型更新后,视图模型通过通知机制(如属性变化通知)更新视图。
- 视图更新以反映最新的数据。
👁️👁️优点:
- 双向数据绑定:提供更简洁的方式来连接UI和数据,自动更新。
- 可测试性高:视图模型包含了用户界面的具体逻辑,易于单元测试。
- 分离关注点:模型、视图和视图模型之间的分离使得代码结构清晰。
- 复用性:视图模型可以在不同视图间复用,不依赖于具体的UI实现。
4.6 总结
MVVM
模式特别适用于需要复杂用户交互和动态数据展示的场景,如桌面应用程序和Web应用程序,尤其是在使用现代前端框架(如 Angular、Vue、React 等)时。它通过将视图与逻辑分离,提高了代码的可维护性和可扩展性。
五、 MVC vs MVVM
在Qt
框架中,MVC
(模型-视图-控制器)和MVVM
(模型-视图-视图模型)也是常见的设计模式。虽然Qt
主要倾向于使用MVVM
,但二者之间仍然存在一些关键区别。下面详细介绍这两者在Qt中的应用和差异。
5.1 MVC(模型-视图-控制器)
👁️👁️结构:
- Model: 数据结构和业务逻辑,通常用
QAbstractItemModel
及其子类(如QStandardItemModel
)来实现。 - View: 界面元素,负责数据的展示。
Qt
提供了多种视图类(如QTableView、QListView
等)。 - Controller: 管理模型和视图之间的交互,但在
Qt
中,通常视图直接与模型交互,逻辑会嵌入在信号和槽中。
👁️👁️示例:
在一个简单的联系人管理应用中:
- Model: 使用QAbstractListModel来存储联系人信息。
- View: 使用QListView来展示联系人列表。
- Controller: 视图接收输入(例如,添加新联系人),然后更新模型,通常是通过信号和槽的机制。
5.2 MVVM (模型-视图-视图模型)在Qt中的使用
👁️👁️结构:
- Model: 同样负责数据和业务逻辑,使用QAbstractItemModel等实现。
- View: 界面部分,通常使用QML(Qt Modeling Language)或Qt Widgets。
- ViewModel: 负责将Model的数据转换为View所需的数据格式和行为。这可以通过QObject和Q_PROPERTY来实现,与数据绑定特性兼容。
👁️👁️示例:
在一个更复杂的任务管理应用中:
- Model: 使用QAbstractListModel实现任务数据。
- View: QML界面显示任务列表。
- ViewModel: 一个QObject子类,提供与UI组件绑定的属性,处理用户输入(如添加或删除任务)。
5.3 区别总结
-
数据绑定:
- MVC: 视图和模型之间的关系更加直接,通常需要手动更新视图。
- MVVM: 可以利用Qt的信号和槽机制,以及Q_PROPERTY,实现更简便的数据绑定。
-
解耦:
- MVC: 控制器和视图之间的关系较紧密,可能导致代码变得复杂。
- MVVM: 通过ViewModel实现较好的解耦,增强了代码的可测试性和可维护性。
-
适用场景:
- MVC: 更适合简单的桌面应用或较少交互的应用。
- MVVM: 适合更复杂、交互性较强的应用,尤其是使用QML的应用。
👁️👁️总结:
在Qt中,尽管MVC模式仍然适用,但MVVM因其更好的可维护性和可扩展性,尤其在使用QML时,更加广泛使用。选择哪种模式通常取决于项目的复杂性、需求以及团队的技术栈。
相关文章:

Qt数据和视图分离——中MCV和MVVM
智能指针 一、背景知识二、命令式编程 vs 声明式编程2.1 命令式编程(Imperative Programming)2.2 声明式编程(Declarative Programming) 三、 MVC(Model-View-Controller)3.1 模型(Model)3.2 视图ÿ…...

重定义变量类型:如#define FLOAT float和typedef float FLOAT的区别
在 C 或 C 中, #define 和 typedef 都可以用来为类型或值创建别名,但它们之间存在一些关键的区别: 预处理指令 ( #define ): #define 是预处理器指令,用于定义宏。 当编译器处理源代码时,预处理器会先运行&#…...

Qt 使用阿里矢量图标库
前言 阿里矢量图标库非常好用,里面有各种丰富的图标,完全免费,还支持自定义图标,还可以将图标打包到一个项目中,使用起来非常方便。 第一步: 打开阿里矢量图标库 第二步: 搜索图标&#x…...

仓颉语言运行时轻量化实践
杨勇勇 华为语言虚拟机实验室架构师,目前负责仓颉语言静态后端的开发工作 仓颉语言运行时轻量化实践 仓颉Native后端(CJNative)是仓颉语言的高性能、轻量化实现。这里的“轻量化”意指仓颉程序运行过程中占用系统资源(内存、CPU等…...

深入理解Python中的subprocess模块
目录 subprocess模块简介常用函数执行外部命令管道通信子进程管理错误处理实际应用示例最佳实践 subprocess模块简介 subprocess模块是Python标准库的一部分,提供了一个跨平台的方法来生成新进程、连接其输入/输出/错误管道,并获取其返回码。该模块旨…...

从零开始搭建 EMQX 集群压测框架
从零开始搭建 EMQX 集群压测框架 架构 在设计以EMQX为中心的MQTT消息队列集群压力测试框架时,我们采用微服务架构模式。EMQX作为消息队列的核心,负责处理MQTT协议的消息发布和订阅。Nginx作为EMQX的反向代理,负责负载均衡和SSL/TLS终端。MQT…...

ArkUI基本介绍
ArkUI:提供HarmonyOS应用UI开发框架,几件开发、精致体验、跨设备/跨平台。 ArkUI(方舟UI框架)为应用的UI开发提供了完整的基础设施,包括简洁的UI语法、丰富的UI功能(组件、布局、动画以及交互事件ÿ…...

vue2+OpenLayers 天地图上打点并且显示相关的信息(2)
上次是在地图上打点 这次鼠标移动在图标上面显示相关的信息 首先有两个事件 鼠标移入 和 鼠标移出事件 pointermove pointerout 鼠标放上去之前 放上去后 代码如下 <template><div class"container"><div id"vue-openlayers" class&quo…...

c++继承(二)
一、友元函数的继承 友元函数不能被继承,就像爸爸的朋友不是你的朋友,如果要有友元函数,在子类重新定义一个。 二、静态成员的继承 静态成员的继承仍然是那个成员,普通成员的继承是不同的。 父类的静态成员属于当前类…...

低代码开发的崛起:机遇与挑战
近年来,“低代码”开发平台的迅速崛起,已经成为IT行业中不可忽视的趋势。这些平台承诺让非专业人士也能快速构建应用程序,通过减少代码编写的需求,大幅提高开发效率。对于许多企业而言,低代码开发工具成为了一个加速数…...

Json-JacksonUtils工具类
为了创建一个通用的 Jackson 工具类,我们可以定义一个名为 JacksonUtils 的工具类,该类将提 供多种方法来支持不同类型的 JSON 转换需求。下面是一个示例实现,包括基本的 JSON 到 Java 对象的转换、Java 对象到 JSON 的转换、以及更复杂的类型如 CommonResult 的转换。 C…...

svn客户端装完后没有svn.exe
如果SVN客户端(如TortoiseSVN)安装完成后,在预期的安装目录(通常是bin目录)中没有找到svn.exe文件,这通常是因为在安装过程中没有选择安装命令行客户端工具(Command Line Client Toolsÿ…...

TinyWebserver的复现与改进(4):主线程的具体实现
GitHub - yzfzzz/MyWebServer: Linux高并发服务器项目,参考了TinyWebServer,将在此基础上进行性能改进与功能增加。为方便读者学习,附带详细注释和博客! TinyWebserver的复现与改进(1):服务器环…...

DaemonSet 不能帮助我们做什么事情?
DaemonSet 不能帮助我们做什么事情? A. 保证集群内每一个(或者一些)节点都运行一组相同的Pod B. 跟踪集群节点状态,保证新加入的节点自动创建对应的Pod C. 跟踪集群节点状态,保证移除的节点删除对应的Pod D. 能够设置Pod重试次数,…...

开源模型应用落地-LangChain高阶-记忆组件-RedisChatMessageHistory正确使用(八)
一、前言 LangChain 的记忆组件发挥着至关重要的作用,其旨在协助大语言模型(LLM)有效地留存历史对话信息。通过这一功能,使得大语言模型在对话过程中能够更出色地维持上下文的连贯性和一致性,进而能够像人类的记忆运作方式那样,进行更为自然、流畅且智能化的交互。 它仿佛…...

解决Openwrt 串口默认是没有密码的方法
将串口登录加入密码方法如下: 步骤一:配置busybox的登录,可以在.config文件中添加如下 CONFIG_BUSYBOX_CONFIG_LOGINy 添加后,需要重新编译busybox。 步骤二:修改target/linux/ramips/base-files/etc/inittab文件 将…...

【vue讲解:v-model 之 lazy、number、trim、与后端交互、小电影案例】
2 v-model 之 lazy、number、trim lazy:等待input框的数据绑定时区焦点之后再变化 number:数字开头,只保留数字,后面的字母不保留;字母开头,都保留 trim:去除首位的空格<!DOCTYPE html> …...

ECCV 2024 | 南洋理工三维数字人生成新范式:结构扩散模型
该论文作者均来自于新加坡南洋理工大学 S-Lab 团队,包括博士后胡涛,博士生洪方舟,以及计算与数据学院刘子纬教授(《麻省理工科技评论》亚太地区 35 岁以下创新者)。S-Lab 近年来在顶级会议如 CVPR, ICCV, ECCV, NeurIP…...

2024.8.13-算法学习(原创+转载)
一、什么是张量并行(Tensor Parallelism) ? 张量并行(Tensor Parallelism) 是一种分布式矩阵算法。 随着模型越来越大,模型内的矩阵也越来越大。一个大矩阵的乘法可以拆分成多个小矩阵的运算,…...

beautifulsoup的简单使用
文章目录 beautifulsoup一. beautifulsoup的简单使用1、安装2、如何使用3、对象的种类 二、beautifulsoup的遍历文档树2.1 子节点.contents 和 .children descendants2.2 节点内容.string.text 2.3 多个内容.strings**.stripped_strings** 2.4 父节点.parent.parents 三、beaut…...

【Python】Jupyter Notebook的安装及简单使用
Jupyter Notebook的安装及简单使用1、安装2、language设置为中文3、Jupyter Notebook启动4、Jupyter Notebook的常用快捷方式5、将Notebook笔记转为其他文件格式保存 Jupyter Notebook的安装及简单使用 不安装AnaCoda,但需要使用Jupyter Notebook 1、安装 pip inst…...

中国自动驾驶出租车冲击网约车市场
近年来,中国的自动驾驶技术迅速发展,对传统网约车市场构成了越来越大的冲击。随着科技巨头百度旗下的萝卜快跑等公司加速推广无人驾驶出租车,这一趋势引发了广泛的讨论和担忧。 自动驾驶技术的迅猛发展 中国自动驾驶行业正处于快速发展阶段&…...

解决浏览器书签同步问题,极空间部署开源免费的跨平台书签同步工具『xBrowserSync』
解决浏览器书签同步问题,极空间部署开源免费的跨平台书签同步工具『xBrowserSync』 哈喽小伙伴们好,我是Stark-C~ 作为一个喜欢折腾的数码党,我平时上网冲浪使用的浏览器绝不会只限于一种,就比如说我在上班的地方只会用到Edge浏…...

14个SpringBoot优化小妙招
今天我们来分享一下平时用SpringBoot开发时候的一些优化小妙招,用好这些优化小妙招让我们开发的系统架构、系统代码、开发流程、测试流程、运维监控看起来就跟写诗一样优雅,让我们每个人手头负责的代码和工程都要很漂亮~~~ 这里的优化小妙招很多不是说直…...

Elasticsearch 度量(Metric)聚合详解及示例
Elasticsearch 提供了强大的聚合功能,允许用户对数据进行深入的统计分析。度量(Metric)聚合是其中一种,它用于对数值型数据进行计算,如求和、平均值、最大值、最小值等。本文将详细介绍 Elasticsearch 的度量聚合&…...

基于 jsp 的健身俱乐部会员系统设计与实现
点击下载源码 基于 jsp 的健身俱乐部会员系统设计与实现 摘 要 目前我国虽然己经开发出了应用计算机操作的健身俱乐部管理系统,但管理软件,管理方法和管理思想三者往往相脱节。造成我国健身俱乐部信息管理系统极端化的缺陷。在国外健身俱乐部已经有了一…...

苍穹外卖项目DAY01
苍穹外卖项目Day01 1、软件开发整体介绍 1.1、软件开发流程 1.2、角色分工 项目经理:对整个项目负责,任务分配、把控进度产品经理:进行需求调研,输出需求调研文档、产品原型等UI设计师:根据产品原型输出界面效果图架…...

SpringBoot(Ⅰ)——HelloWorld和基本打包部署+Pom依赖概述+@SpringBootApplication注解+自动装配原理+约定大于配置
前言 如果SSM学的比较好,那么SpringBoot说白了就两件事:约定大于配置和自动装配 SpringBoot不会提供任何的功能拓展,完全依赖我们手动添加 所以SpringBoot的本质是一个依赖脚手架,可以快速集成配置各种依赖 1.1 SpringBoot相关依赖 创建…...

[Unity]关闭URP的SRP,开启GPU Instancing。
1. 对应材质的gpu instancing勾选上。 2. 游戏初始化时动态关闭SRP,或者在Graphics里全局关闭。动态关闭的代码如下: GraphicsSettings.useScriptableRenderPipelineBatching false; 模型合批的一些规则: 1. 模型一致。 2. 材质一致。 …...

04创建型设计模式——建造者模式
一、建造者模式简介 建造者模式(Builder Pattern)又被称为生成器模式。它旨在构建一个复杂对象的各个部分,而不需要指定该对象的具体类。该模式特别适用于对象的构建过程复杂且需要多个步骤的情况。建造者模式是一种对象创建型模式之一&…...