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
的概念,我们可以把界面相关的操作进一步封装,并且更明确地展示 View
、ViewModel
和 Model
之间的交互。以下是改进后的代码:
项目结构
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
槽函数:当用户点击按钮时,获取输入框中的文本,然后调用ViewModel
的setViewModelData
方法更新数据。updateLabel
槽函数:当ViewModel
的displayData
发生变化时,更新界面上的标签文本。
ViewModel
类
- 作为
View
和Model
之间的桥梁,负责处理业务逻辑和数据转换。当Model
的数据发生变化时,会发出displayDataChanged
信号通知View
更新。
Model
类
- 负责存储和管理数据,当数据发生变化时发出
dataChanged
信号。
main.cpp
- 创建
Model
、ViewModel
和View
对象,并显示View
。
通过这种方式,我们清晰地划分了 View
、ViewModel
和 Model
的职责,实现了 MVVM 模式。