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

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_OBJECT
    Q_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_OBJECT
    Q_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_NAMESPACE

class View : public QWidget
{
    Q_OBJECT

public:
    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_OBJECT
    Q_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_OBJECT
    Q_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_NAMESPACE

class View : public QWidget
{
    Q_OBJECT

public:
    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 模式。


http://www.kler.cn/a/540808.html

相关文章:

  • 前端权限控制和管理
  • 推荐一个免费的、开源的大数据工程学习教程
  • 嵌入式linux系统中VIM编辑工具用法与GCC参数详解
  • DeepSeek 实践总结
  • 白话文实战Nacos(保姆级教程)
  • DeepSeek神经网络:技术架构与实现原理探析
  • 大模型基础面试问题汇总
  • 1.2 环境搭建
  • 「vue3-element-admin」告别 vite-plugin-svg-icons!用 @unocss/preset-icons 加载本地 SVG 图标
  • 2.1 Mockito核心API详解
  • PriorityQueue优先级队列的使用和Top-k问题
  • 小白零基础学习深度学习之张量
  • 【C++语言】类和对象(下)
  • 备战蓝桥杯:二分算法详解以及模板题
  • Redis持久化机制详解
  • Proxy vs DefineProperty
  • 车载工具报错分析:CANoe、CANalyzer问题:Stuff Error
  • Java 大视界 -- Java 大数据在智能家居中的应用与场景构建(79)
  • Vue:Table合并行于列
  • 用Go实现 SSE 实时推送消息(消息通知)——思悟项目技术4
  • 绘制中国平安股价的交互式 K 线图
  • 31、spark-on-kubernetes中任务报错No space left on device
  • Fastadmin根据链接参数显示不同列表格
  • 10 FastAPI 的自动文档
  • OpenAI 实战进阶教程 - 第十二节 : 多模态任务开发(文本、图像、音频)
  • 持续集成-笔记