当前位置: 首页 > news >正文

MVVM设计模式

‌MVVM(Model-View-ViewModel)是一种软件设计模式,MVVM模式由三个主要部分组成:

‌Model(模型)‌:负责管理应用程序的业务逻辑和数据。它不关心UI如何展示数据,主要负责与服务器通信和数据处处理‌。
‌View(视图)‌:直接与用户交互的界面,负责展示数据。视图不包含业务逻辑,只负责数据的展示‌。
‌ViewModel(视图模型)‌:作为视图和模型之间的桥梁,包含UI逻辑,通过数据绑定机制驱动视图的变化。它处理用户的输入并将结果传递给模型‌。

MVVM的核心机制和优势
‌数据绑定‌是MVVM的核心机制,它允许视图和视图模型之间的自动同步,无需手动编写繁琐的代码来更新界面‌。
这种设计模式的主要优势包括:
‌解耦‌:视图和模型完全解耦,视图模型管理两者的交互,提高了代码的可维护性和扩展性‌。
‌可测试性‌:由于视图模型不依赖于视图,可以通过单元测试来验证业务逻辑的正确性,而不需要启动UI环境‌。
‌提高开发效率‌:通过数据绑定和事件处理,简化了开发过程,减少了手动同步数据的需要‌。

项目结构

mvvm_example/
├── main.cpp
├── view.h
├── view.cpp
├── viewmodel.h
├── viewmodel.cpp
├── model.h
└── model.cpp

代码实现
model.h

#ifndef MODEL_H
#define MODEL_H#include <QObject>
#include <QString>class Model : public QObject
{Q_OBJECTQ_PROPERTY(QString data READ data WRITE setData NOTIFY dataChanged)public:explicit Model(QObject *parent = nullptr);QString data() const;void setData(const QString &newData);signals:void dataChanged();private:QString m_data;
};#endif // MODEL_H

model.cpp

#include "model.h"Model::Model(QObject *parent) : QObject(parent)
{
}QString Model::data() const
{return m_data;
}void Model::setData(const QString &newData)
{if (m_data != newData) {m_data = newData;emit dataChanged();}
}

viewmodel.h

#ifndef VIEWMODEL_H
#define VIEWMODEL_H#include <QObject>
#include "model.h"class ViewModel : public QObject
{Q_OBJECTQ_PROPERTY(QString displayData READ displayData NOTIFY displayDataChanged)public:explicit ViewModel(Model *model, QObject *parent = nullptr);QString displayData() const;signals:void displayDataChanged();public slots:void onModelDataChanged();void setViewModelData(const QString &newData);private:Model *m_model;
};#endif // VIEWMODEL_H

viewmodel.cpp

#include "viewmodel.h"ViewModel::ViewModel(Model *model, QObject *parent) : QObject(parent), m_model(model)
{connect(m_model, &Model::dataChanged, this, &ViewModel::onModelDataChanged);
}QString ViewModel::displayData() const
{return m_model->data();
}void ViewModel::onModelDataChanged()
{emit displayDataChanged();
}void ViewModel::setViewModelData(const QString &newData)
{m_model->setData(newData);
}

view.h

#ifndef VIEW_H
#define VIEW_H#include <QWidget>
#include "viewmodel.h"QT_BEGIN_NAMESPACE
namespace Ui { class View; }
QT_END_NAMESPACEclass View : public QWidget
{Q_OBJECTpublic:explicit View(ViewModel *viewModel, QWidget *parent = nullptr);~View();private slots:void on_pushButton_clicked();void updateLabel(const QString &data);private:Ui::View *ui;ViewModel *m_viewModel;
};
#endif // VIEW_H

view.cpp

#include "view.h"
#include "ui_view.h"View::View(ViewModel *viewModel, QWidget *parent): QWidget(parent), ui(new Ui::View), m_viewModel(viewModel)
{ui->setupUi(this);// 绑定 ViewModel 的 displayDataChanged 信号到更新标签的槽函数connect(m_viewModel, &ViewModel::displayDataChanged, this, [this]() {updateLabel(m_viewModel->displayData());});// 绑定按钮点击信号到槽函数connect(ui->pushButton, &QPushButton::clicked, this, &View::on_pushButton_clicked);
}View::~View()
{delete ui;
}void View::on_pushButton_clicked()
{QString input = ui->lineEdit->text();m_viewModel->setViewModelData(input);
}void View::updateLabel(const QString &data)
{ui->label->setText(data);
}

main.cpp

#include "view.h"
#include "model.h"
#include "viewmodel.h"
#include <QApplication>int main(int argc, char *argv[])
{QApplication a(argc, argv);Model model;ViewModel viewModel(&model);View view(&viewModel);view.show();return a.exec();
}

在上述代码里,MainWindow 类实际上就充当了 View 的角色。不过为了更清晰地体现 View 的概念,我们可以把界面相关的操作进一步封装,并且更明确地展示 ViewViewModelModel 之间的交互。以下是改进后的代码:

项目结构

mvvm_example/
├── main.cpp
├── view.h
├── view.cpp
├── viewmodel.h
├── viewmodel.cpp
├── model.h
└── model.cpp

代码实现

model.h
#ifndef MODEL_H
#define MODEL_H#include <QObject>
#include <QString>class Model : public QObject
{Q_OBJECTQ_PROPERTY(QString data READ data WRITE setData NOTIFY dataChanged)public:explicit Model(QObject *parent = nullptr);QString data() const;void setData(const QString &newData);signals:void dataChanged();private:QString m_data;
};#endif // MODEL_H
model.cpp
#include "model.h"Model::Model(QObject *parent) : QObject(parent)
{
}QString Model::data() const
{return m_data;
}void Model::setData(const QString &newData)
{if (m_data != newData) {m_data = newData;emit dataChanged();}
}
viewmodel.h
#ifndef VIEWMODEL_H
#define VIEWMODEL_H#include <QObject>
#include "model.h"class ViewModel : public QObject
{Q_OBJECTQ_PROPERTY(QString displayData READ displayData NOTIFY displayDataChanged)public:explicit ViewModel(Model *model, QObject *parent = nullptr);QString displayData() const;signals:void displayDataChanged();public slots:void onModelDataChanged();void setViewModelData(const QString &newData);private:Model *m_model;
};#endif // VIEWMODEL_H
viewmodel.cpp
#include "viewmodel.h"ViewModel::ViewModel(Model *model, QObject *parent) : QObject(parent), m_model(model)
{connect(m_model, &Model::dataChanged, this, &ViewModel::onModelDataChanged);
}QString ViewModel::displayData() const
{return m_model->data();
}void ViewModel::onModelDataChanged()
{emit displayDataChanged();
}void ViewModel::setViewModelData(const QString &newData)
{m_model->setData(newData);
}
view.h
#ifndef VIEW_H
#define VIEW_H#include <QWidget>
#include "viewmodel.h"QT_BEGIN_NAMESPACE
namespace Ui { class View; }
QT_END_NAMESPACEclass View : public QWidget
{Q_OBJECTpublic:explicit View(ViewModel *viewModel, QWidget *parent = nullptr);~View();private slots:void on_pushButton_clicked();void updateLabel(const QString &data);private:Ui::View *ui;ViewModel *m_viewModel;
};
#endif // VIEW_H
view.cpp
#include "view.h"
#include "ui_view.h"View::View(ViewModel *viewModel, QWidget *parent): QWidget(parent), ui(new Ui::View), m_viewModel(viewModel)
{ui->setupUi(this);// 绑定 ViewModel 的 displayDataChanged 信号到更新标签的槽函数connect(m_viewModel, &ViewModel::displayDataChanged, this, [this]() {updateLabel(m_viewModel->displayData());});// 绑定按钮点击信号到槽函数connect(ui->pushButton, &QPushButton::clicked, this, &View::on_pushButton_clicked);
}View::~View()
{delete ui;
}void View::on_pushButton_clicked()
{QString input = ui->lineEdit->text();m_viewModel->setViewModelData(input);
}void View::updateLabel(const QString &data)
{ui->label->setText(data);
}
main.cpp
#include "view.h"
#include "model.h"
#include "viewmodel.h"
#include <QApplication>int main(int argc, char *argv[])
{QApplication a(argc, argv);Model model;ViewModel viewModel(&model);View view(&viewModel);view.show();return a.exec();
}

代码解释

View
  • 构造函数:接收一个 ViewModel 对象的指针,负责初始化界面并建立信号与槽的连接。
  • on_pushButton_clicked 槽函数:当用户点击按钮时,获取输入框中的文本,然后调用 ViewModelsetViewModelData 方法更新数据。
  • updateLabel 槽函数:当 ViewModeldisplayData 发生变化时,更新界面上的标签文本。
ViewModel
  • 作为 ViewModel 之间的桥梁,负责处理业务逻辑和数据转换。当 Model 的数据发生变化时,会发出 displayDataChanged 信号通知 View 更新。
Model
  • 负责存储和管理数据,当数据发生变化时发出 dataChanged 信号。
main.cpp
  • 创建 ModelViewModelView 对象,并显示 View

通过这种方式,我们清晰地划分了 ViewViewModelModel 的职责,实现了 MVVM 模式。

相关文章:

MVVM设计模式

‌MVVM&#xff08;Model-View-ViewModel&#xff09;是一种软件设计模式,MVVM模式由三个主要部分组成&#xff1a; ‌Model&#xff08;模型&#xff09;‌&#xff1a;负责管理应用程序的业务逻辑和数据。它不关心UI如何展示数据&#xff0c;主要负责与服务器通信和数据处处…...

解决:Cannot find a valid baseurl for repo: base/7/x86_64

传送门 repo_file/etc/yum.repos.d/CentOS-Base.repo cp ${repo_file} ~/CentOS-Base.repo.backup sudo sed -i s/#baseurl/baseurl/ ${repo_file} sudo sed -i s/mirrorlist.centos.org/vault.centos.org/ ${repo_file} sudo sed -i s/mirror.centos.org/vault.centos.org/ $…...

ffmpeg -codecs

1. ffmpeg -codecs -loglevel quiet 显示ffmpeg支持的编解码器 2. 输出 选取部分结果&#xff1a; Codecs: D..... Decoding supported .E.... Encoding supported ..V... Video codec ..A... Audio codec ..S... Subtitle codec ...I.. Intra frame-only code…...

社区版IDEA中配置TomCat(详细版)

文章目录 1、下载Smart TomCat2、配置TomCat3、运行代码 1、下载Smart TomCat 由于小编的是社区版&#xff0c;没有自带的tomcat server&#xff0c;所以在设置的插件里面搜索&#xff0c;安装第一个&#xff08;注意&#xff1a;安装时一定要关闭外网&#xff0c;小编因为这个…...

强化学习 DPO 算法:基于人类偏好,颠覆 PPO 传统策略

目录 一、引言二、强化学习基础回顾&#xff08;一&#xff09;策略&#xff08;二&#xff09;价值函数 三、近端策略优化&#xff08;PPO&#xff09;算法&#xff08;一&#xff09;算法原理&#xff08;二&#xff09;PPO 目标函数&#xff08;三&#xff09;代码示例&…...

长安链支撑全国不动产登记数据可信流通

转自人民日报客户端 不动产登记事关亿万企业、家庭的切身利益。促进不动产登记数据安全流通、业务高效协同&#xff0c;是各方持续努力的目标。记者1月7日从国家区块链技术创新中心获悉&#xff0c;我国自主可控、性能领先的区块链软硬件技术体系长安链&#xff0c;支撑自然资…...

GitCode 助力 Dora SSR:开启游戏开发新征程

项目仓库&#xff08;点击阅读原文链接可直达&#xff09; https://gitcode.com/ippclub/Dora-SSR 跨越技术藩篱&#xff0c;构建游戏开发乐园 Dora SSR 是一款致力于打破游戏开发技术壁垒的开源游戏引擎。其诞生源于开发者对简化跨平台游戏开发环境搭建的强烈渴望&#xff0…...

获取 Windows 视频时长的正确方式——Windows Shell API 深度解析

在 Qt 开发中,有时需要获取视频文件的时长,最直接的方法是在 Windows 上使用 Windows Shell API。然而,这涉及到 IShellItem、IPropertyStore 等 COM 组件,并需要正确处理 PKEY_Media_Duration。本篇文章将详细解析 Windows Shell API 获取视频时长的正确实现方式,并解决常…...

Linux系统安装Nginx详解(适用于CentOS 7)

目录 1. 更新系统包 2. 安装EPEL仓库 3. 安装Nginx 4. 启动Nginx服务 5. 设置Nginx开机自启 6. 检查Nginx状态 7. 配置防火墙 8. 访问Nginx默认页面 9. 配置Nginx&#xff08;可选&#xff09; 10. 重启Nginx 解决步骤 1. 检查系统版本 2. 移除错误的 Nginx 仓库 …...

深入理解Java对接DeepSeek

其实&#xff0c;整个对接过程很简单&#xff0c;就四步&#xff0c;获取key&#xff0c;找到接口文档&#xff0c;接口测试&#xff0c;代码对接。 1.获取 KEY https://platform.deepseek.com/transactions 直接付款就是了&#xff08;现在官网暂停充值2025年2月7日&#xf…...

flutter isolate到底是啥

在 Flutter 中&#xff0c;Isolate 是一种实现多线程编程的机制&#xff0c;下面从概念、工作原理、使用场景、使用示例几个方面详细介绍&#xff1a; 概念 在 Dart 语言&#xff08;Flutter 开发使用的编程语言&#xff09;里&#xff0c;每个 Dart 程序至少运行在一个 Isol…...

深入剖析 Apache Shiro550 反序列化漏洞及复现

目录 前言 一、认识 Apache Shiro 二、反序列化漏洞&#xff1a;隐藏在数据转换中的风险 三、Shiro550 漏洞&#xff1a;会话管理中的致命缺陷 四、漏洞危害&#xff1a;如多米诺骨牌般的连锁反应 五、漏洞复现&#xff1a;揭开攻击的神秘面纱 &#xff08;一&#xff0…...

计算机毕业设计——Springboot的简历系统

&#x1f4d8; 博主小档案&#xff1a; 花花&#xff0c;一名来自世界500强的资深程序猿&#xff0c;毕业于国内知名985高校。 &#x1f527; 技术专长&#xff1a; 花花在深度学习任务中展现出卓越的能力&#xff0c;包括但不限于java、python等技术。近年来&#xff0c;花花更…...

【kubernetes组件合集】深入解析Kubernetes组件之三:client-go

深入解析Kubernetes组件之三&#xff1a;client-go 目录 深入解析Kubernetes组件之三&#xff1a;client-go 引言 1. client-go简介 2. client-go的功能 2.1 资源操作 2.2 资源监听 2.3 认证和授权 2.4 错误处理和重试 2.5 扩展性和定制化 3. 使用client-go与Kubern…...

线程池-抢票系统性能优化

文章目录 引言-购票系统线程池购票系统-线程池优化 池化 vs 未池化 引言-购票系统 public class App implements Runnable {private static int tickets 100;private static int users 10000;private final ReentrantLock lock new ReentrantLock(true);public void run() …...

WebSocket 握手过程

文章目录 1. WebSocket 握手过程概述2. 客户端发送握手请求3. 服务器响应握手请求4. 客户端验证握手响应5. 建立 WebSocket 连接6. 安全性与注意事项7. 应用示例 在现代 Web 开发中&#xff0c;WebSocket 协议因其高效的实时通信能力而被广泛应用。WebSocket 允许客户端和服务器…...

VMware 虚拟机 ubuntu 20.04 扩容工作硬盘

一、关闭虚拟机 关闭虚拟机参考下图&#xff0c;在vmware 调整磁盘容量 二、借助工具fdisk testubuntu ~ $ df -h Filesystem Size Used Avail Use% Mounted on udev 1.9G 0 1.9G 0% /dev tmpfs 388M 3.1M 385M 1% /run /dev/sda5 …...

备战蓝桥杯:二分算法之牛可乐和魔法封印问题

这道题就是一道简单的模板题&#xff0c;我们分析一下&#xff01;&#xff0c;首先我们要找大于等于x的起始位置 我们还是用两个指针&#xff0c;left指向1&#xff0c;right指向n&#xff0c;如果a[mid]<x left mid1 如果a[mid]>x 就让right mid 如果数组全是小于x…...

普通用户授权docker使用权限

1、检查docker用户组 sudo cat /etc/group |grep docker 若显示&#xff1a;docker:x:999: # 表示存在否则创建docker用户组&#xff1a; sudo groupadd docker2、查看 /var/run/docker.sock 的属性 ll /var/run/docker.sock 显示&#xff1a; srw-rw---- 1 root root 0 1月…...

【实战篇】DeepSeek + ElevenLabs:让人工智能“开口说话”,打造你的专属语音助手!

最近,AI语音合成技术真是火得不行,各种“开口脆”的AI声音层出不穷,听得我直呼“这也太像真人了吧!” 作为一个科技爱好者,我当然不能错过这股潮流,这不,最近就沉迷于用 DeepSeek 和 ElevenLabs 这两款神器,捣鼓各种人声音频,简直停不下来! 先来科普一下这两位“主角…...

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…...

(十)学生端搭建

本次旨在将之前的已完成的部分功能进行拼装到学生端&#xff0c;同时完善学生端的构建。本次工作主要包括&#xff1a; 1.学生端整体界面布局 2.模拟考场与部分个人画像流程的串联 3.整体学生端逻辑 一、学生端 在主界面可以选择自己的用户角色 选择学生则进入学生登录界面…...

MFC内存泄露

1、泄露代码示例 void X::SetApplicationBtn() {CMFCRibbonApplicationButton* pBtn GetApplicationButton();// 获取 Ribbon Bar 指针// 创建自定义按钮CCustomRibbonAppButton* pCustomButton new CCustomRibbonAppButton();pCustomButton->SetImage(IDB_BITMAP_Jdp26)…...

java 实现excel文件转pdf | 无水印 | 无限制

文章目录 目录 文章目录 前言 1.项目远程仓库配置 2.pom文件引入相关依赖 3.代码破解 二、Excel转PDF 1.代码实现 2.Aspose.License.xml 授权文件 总结 前言 java处理excel转pdf一直没找到什么好用的免费jar包工具,自己手写的难度,恐怕高级程序员花费一年的事件,也…...

[ICLR 2022]How Much Can CLIP Benefit Vision-and-Language Tasks?

论文网址&#xff1a;pdf 英文是纯手打的&#xff01;论文原文的summarizing and paraphrasing。可能会出现难以避免的拼写错误和语法错误&#xff0c;若有发现欢迎评论指正&#xff01;文章偏向于笔记&#xff0c;谨慎食用 目录 1. 心得 2. 论文逐段精读 2.1. Abstract 2…...

华为OD机试-食堂供餐-二分法

import java.util.Arrays; import java.util.Scanner;public class DemoTest3 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseint a in.nextIn…...

Mac软件卸载指南,简单易懂!

刚和Adobe分手&#xff0c;它却总在Library里给你写"回忆录"&#xff1f;卸载的Final Cut Pro像电子幽灵般阴魂不散&#xff1f;总是会有残留文件&#xff0c;别慌&#xff01;这份Mac软件卸载指南&#xff0c;将用最硬核的方式教你"数字分手术"&#xff0…...

MySQL 8.0 OCP 英文题库解析(十三)

Oracle 为庆祝 MySQL 30 周年&#xff0c;截止到 2025.07.31 之前。所有人均可以免费考取原价245美元的MySQL OCP 认证。 从今天开始&#xff0c;将英文题库免费公布出来&#xff0c;并进行解析&#xff0c;帮助大家在一个月之内轻松通过OCP认证。 本期公布试题111~120 试题1…...

UR 协作机器人「三剑客」:精密轻量担当(UR7e)、全能协作主力(UR12e)、重型任务专家(UR15)

UR协作机器人正以其卓越性能在现代制造业自动化中扮演重要角色。UR7e、UR12e和UR15通过创新技术和精准设计满足了不同行业的多样化需求。其中&#xff0c;UR15以其速度、精度及人工智能准备能力成为自动化领域的重要突破。UR7e和UR12e则在负载规格和市场定位上不断优化&#xf…...

#Uniapp篇:chrome调试unapp适配

chrome调试设备----使用Android模拟机开发调试移动端页面 Chrome://inspect/#devices MuMu模拟器Edge浏览器&#xff1a;Android原生APP嵌入的H5页面元素定位 chrome://inspect/#devices uniapp单位适配 根路径下 postcss.config.js 需要装这些插件 “postcss”: “^8.5.…...