理解MVC我们从下面的一个小例子开始。
1. 需求
实现一个加法计算器,如下图:


2. 最简单的实现方法
2.1 实现方法
将界面和逻辑写在一起
2.2 出现的问题
- 逻辑相对稳定,界面却多变。
- 修改界面代码,将不得不修改逻辑代码(如下面的代码),可能会引入bug,增加测试工作。
- 代码不优雅。一旦项目变得复杂,代码将很乱,难以维护。
2.3 代码(界面1)

- view.h
c++
#ifndef VIEW_H
#define VIEW_H
#include <QWidget>
#include <QVBoxLayout>
#include <QPushButton>
#include <QLineEdit>
class View : public QWidget
{
Q_OBJECT
public:
View(QWidget *parent = nullptr);
~View();
private:
QVBoxLayout *m_layout;
QLineEdit *m_valueEdit_1;
QLineEdit *m_valueEdit_2;
QLineEdit *m_sumEdit;
QPushButton *m_addBtn;
private slots:
void slot_addBtn(bool);
};
#endif // VIEW_H
- view.cpp
c++
#include "view.h"
View::View(QWidget *parent)
: QWidget(parent)
{
//ui
m_layout = new QVBoxLayout(this);
m_valueEdit_1 = new QLineEdit();
m_valueEdit_2 = new QLineEdit();
m_sumEdit = new QLineEdit();
m_addBtn = new QPushButton("Add");
m_layout->addWidget(m_valueEdit_1);
m_layout->addWidget(m_valueEdit_2);
m_layout->addWidget(m_sumEdit);
m_layout->addWidget(m_addBtn);
this->resize(300, 200);
//逻辑
connect(m_addBtn, SIGNAL(clicked(bool)), this, SLOT(slot_addBtn(bool)));
}
View::~View()
{
}
void View::slot_addBtn(bool)
{
int v1 = m_valueEdit_1->text().toInt();
int v2 = m_valueEdit_2->text().toInt();
QString sum = QString::number(v1 + v2);
m_sumEdit->setText(sum);
}
- main.cpp
c++
#include "view.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
View w;
w.show();
return a.exec();
}
2.4 代码(界面2)

- view.h
cpp
#ifndef VIEW_H
#define VIEW_H
#include <QWidget>
#include <QVBoxLayout>
#include <QPushButton>
#include <QSlider>
class View : public QWidget
{
Q_OBJECT
public:
View(QWidget *parent = nullptr);
~View();
private:
QVBoxLayout *m_layout;
QSlider *m_valueSlider_1;
QSlider *m_valueSlider_2;
QSlider *m_sumSlider;
QPushButton *m_addBtn;
private slots:
void slot_addBtn(bool);
};
#endif // VIEW_H
- view.cpp
cpp
#include "view.h"
View::View(QWidget *parent)
: QWidget(parent)
{
//ui
m_layout = new QVBoxLayout(this);
m_valueSlider_1 = new QSlider();
m_valueSlider_2 = new QSlider();
m_sumSlider = new QSlider();
m_addBtn = new QPushButton("Add");
m_valueSlider_1->setOrientation(Qt::Horizontal);
m_valueSlider_2->setOrientation(Qt::Horizontal);
m_sumSlider->setOrientation(Qt::Horizontal);
m_layout->addWidget(m_valueSlider_1);
m_layout->addWidget(m_valueSlider_2);
m_layout->addWidget(m_sumSlider);
m_layout->addWidget(m_addBtn);
this->resize(300, 200);
//逻辑
connect(m_addBtn, SIGNAL(clicked(bool)), this, SLOT(slot_addBtn(bool)));
}
View::~View()
{
}
void View::slot_addBtn(bool)
{
int v1 = m_valueSlider_1->value();
int v2 = m_valueSlider_2->value();
int sum = v1 + v2;
m_sumSlider->setValue(sum);
}
- main.cpp(未修改)
3. 改进方法(Model-View, MV)
3.1 改进目标
- 界面与逻辑分离,实现界面修改后逻辑代码一点都不动
3.2 实现方法
- 将
View
和Model
代码分离,逻辑代码放到model
中, 界面代码放在view
中。
3.3 出现的问题
View
依赖于Model
。
3.4 代码(界面1)

- model.h
cpp
#ifndef MODEL_H
#define MODEL_H
#include <QObject>
class Model : public QObject
{
Q_OBJECT
public:
explicit Model(QObject *parent = nullptr);
public slots:
void slot_addFun(int a, int b);
private:
int m_sum;
signals:
void sig_sumUpdate(int sum);
};
#endif // MODEL_H
- model.cpp
cpp
#include "model.h"
Model::Model(QObject *parent)
: QObject{parent}
{
}
void Model::slot_addFun(int a, int b)
{
m_sum = a + b;
emit sig_sumUpdate(m_sum);
}
- view.h
cpp
#ifndef VIEW_H
#define VIEW_H
#include <QWidget>
#include <QVBoxLayout>
#include <QPushButton>
#include <QLineEdit>
#include "model.h"
class View : public QWidget
{
Q_OBJECT
public:
View(Model *model, QWidget *parent = nullptr);
~View();
private:
QVBoxLayout *m_layout;
QLineEdit *m_valueEdit_1;
QLineEdit *m_valueEdit_2;
QLineEdit *m_sumEdit;
QPushButton *m_addBtn;
Model *m_model;
private slots:
void slot_addBtn(bool);
void slot_sumUpdate(int sum);
signals:
void sig_addFun(int a, int b);
};
#endif // VIEW_H
- view.cpp
cpp
#include "view.h"
View::View(Model *model, QWidget *parent)
: QWidget(parent), m_model(model)
{
//ui
m_layout = new QVBoxLayout(this);
m_valueEdit_1 = new QLineEdit();
m_valueEdit_2 = new QLineEdit();
m_sumEdit = new QLineEdit();
m_addBtn = new QPushButton("Add");
m_layout->addWidget(m_valueEdit_1);
m_layout->addWidget(m_valueEdit_2);
m_layout->addWidget(m_sumEdit);
m_layout->addWidget(m_addBtn);
this->resize(300, 200);
//逻辑连接
connect(m_addBtn, SIGNAL(clicked(bool)), this, SLOT(slot_addBtn(bool)));
connect(this, SIGNAL(sig_addFun(int,int)), m_model, SLOT(slot_addFun(int,int)));
connect(m_model, SIGNAL(sig_sumUpdate(int)), this, SLOT(slot_sumUpdate(int)));
}
View::~View()
{
}
void View::slot_addBtn(bool)
{
int v1 = m_valueEdit_1->text().toInt();
int v2 = m_valueEdit_2->text().toInt();
emit sig_addFun(v1, v2);
}
void View::slot_sumUpdate(int sum)
{
m_sumEdit->setText(QString::number(sum));
}
- main.cpp
cpp
#include "view.h"
#include "model.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Model* model = new Model();
View* view = new View(model);
view->show();
return a.exec();
}
3.5 代码(界面2)

- model.h(未修改)
- model.cpp(未修改)
- view.h
cpp
#ifndef VIEW_H
#define VIEW_H
#include <QWidget>
#include <QVBoxLayout>
#include <QPushButton>
#include <QSlider>
#include "model.h"
class View : public QWidget
{
Q_OBJECT
public:
View(Model *model, QWidget *parent = nullptr);
~View();
private:
QVBoxLayout *m_layout;
QSlider *m_valueSlider_1;
QSlider *m_valueSlider_2;
QSlider *m_sumSlider;
QPushButton *m_addBtn;
Model *m_model;
private slots:
void slot_addBtn(bool);
void slot_sumUpdate(int sum);
signals:
void sig_addFun(int a, int b);
};
#endif // VIEW_H
- view.cpp
cpp
#include "view.h"
View::View(Model *model, QWidget *parent)
: QWidget(parent), m_model(model)
{
//ui
m_layout = new QVBoxLayout(this);
m_valueSlider_1 = new QSlider();
m_valueSlider_2 = new QSlider();
m_sumSlider = new QSlider();
m_addBtn = new QPushButton("Add");
m_valueSlider_1->setOrientation(Qt::Horizontal);
m_valueSlider_2->setOrientation(Qt::Horizontal);
m_sumSlider->setOrientation(Qt::Horizontal);
m_layout->addWidget(m_valueSlider_1);
m_layout->addWidget(m_valueSlider_2);
m_layout->addWidget(m_sumSlider);
m_layout->addWidget(m_addBtn);
this->resize(300, 200);
//逻辑连接
connect(m_addBtn, SIGNAL(clicked(bool)), this, SLOT(slot_addBtn(bool)));
connect(this, SIGNAL(sig_addFun(int,int)), m_model, SLOT(slot_addFun(int,int)));
connect(m_model, SIGNAL(sig_sumUpdate(int)), this, SLOT(slot_sumUpdate(int)));
}
View::~View()
{
}
void View::slot_addBtn(bool)
{
int v1 = m_valueSlider_1->value();
int v2 = m_valueSlider_2->value();
emit sig_addFun(v1, v2);
}
void View::slot_sumUpdate(int sum)
{
m_sumSlider->setValue(sum);
}
- main.cpp(未修改)
4. 继续改进(Model-View-Controller, MVC)
4.1 改进目标
- 将
Model
与View
彻底解耦,互相不知道对方的存在。
4.2 实现方法
- 在
View
与Model
中间添加一个中介,即Controller
.
4.3 出现的新问题
- 至此就实现了
MVC
- 出现的新问题就是
MVC
的问题,例如:随着项目越来越复杂,Controller会变得冗长。
4.4 代码(界面1)

- model.h
cpp
#ifndef MODEL_H
#define MODEL_H
#include <QObject>
class Model : public QObject
{
Q_OBJECT
public:
explicit Model(QObject *parent = nullptr);
public slots:
void slot_addFun(int a, int b);
private:
int m_sum;
signals:
void sig_sumUpdate(int sum);
};
#endif // MODEL_H
- model.cpp
cpp
#include "model.h"
Model::Model(QObject *parent)
: QObject{parent}
{
}
void Model::slot_addFun(int a, int b)
{
m_sum = a + b;
emit sig_sumUpdate(m_sum);
}
- view.h
cpp
#ifndef VIEW_H
#define VIEW_H
#include <QWidget>
#include <QVBoxLayout>
#include <QPushButton>
#include <QLineEdit>
class View : public QWidget
{
Q_OBJECT
public:
View(QWidget *parent = nullptr);
~View();
private:
QVBoxLayout *m_layout;
QLineEdit *m_valueEdit_1;
QLineEdit *m_valueEdit_2;
QLineEdit *m_sumEdit;
QPushButton *m_addBtn;
private slots:
void slot_addBtn(bool);
void slot_sumUpdate(int sum);
signals:
void sig_addFun(int a, int b);
};
#endif // VIEW_H
- view.cpp
cpp
#include "view.h"
View::View(QWidget *parent)
: QWidget(parent)
{
//ui
m_layout = new QVBoxLayout(this);
m_valueEdit_1 = new QLineEdit();
m_valueEdit_2 = new QLineEdit();
m_sumEdit = new QLineEdit();
m_addBtn = new QPushButton("Add");
m_layout->addWidget(m_valueEdit_1);
m_layout->addWidget(m_valueEdit_2);
m_layout->addWidget(m_sumEdit);
m_layout->addWidget(m_addBtn);
this->resize(300, 200);
//逻辑
connect(m_addBtn, SIGNAL(clicked(bool)), this, SLOT(slot_addBtn(bool)));
}
View::~View()
{
}
void View::slot_addBtn(bool)
{
int v1 = m_valueEdit_1->text().toInt();
int v2 = m_valueEdit_2->text().toInt();
emit sig_addFun(v1, v2);
}
void View::slot_sumUpdate(int sum)
{
m_sumEdit->setText(QString::number(sum));
}
- controller.h
cpp
#ifndef CONTROLLER_H
#define CONTROLLER_H
#include <QObject>
#include "model.h"
#include "view.h"
class Controller : public QObject
{
Q_OBJECT
public:
explicit Controller(Model *model, View *view, QObject *parent = nullptr);
private:
Model *m_model;
View *m_view;
signals:
};
#endif // CONTROLLER_H
- controller.cpp
cpp
#include "controller.h"
Controller::Controller(Model *model, View *view, QObject *parent)
: QObject{parent}, m_model(model), m_view(view)
{
connect(m_view, SIGNAL(sig_addFun(int,int)), m_model, SLOT(slot_addFun(int,int)));
connect(m_model, SIGNAL(sig_sumUpdate(int)), m_view, SLOT(slot_sumUpdate(int)));
}
- main.cpp
cpp
#include "view.h"
#include "model.h"
#include <QApplication>
#include "model.h"
#include "view.h"
#include "controller.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Model* model = new Model();
View* view = new View();
Controller* controller = new Controller(model, view);
view->show();
return a.exec();
}
4.5 代码(界面2)

- model.h(未修改)
- model.cpp(未修改)
- view.h
cpp
#ifndef VIEW_H
#define VIEW_H
#include <QWidget>
#include <QVBoxLayout>
#include <QPushButton>
#include <QSlider>
class View : public QWidget
{
Q_OBJECT
public:
View(QWidget *parent = nullptr);
~View();
private:
QVBoxLayout *m_layout;
QSlider *m_valueSlider_1;
QSlider *m_valueSlider_2;
QSlider *m_sumSlider;
QPushButton *m_addBtn;
private slots:
void slot_addBtn(bool);
void slot_sumUpdate(int sum);
signals:
void sig_addFun(int a, int b);
};
#endif // VIEW_H
- view.cpp
cpp
#include "view.h"
View::View(QWidget *parent)
: QWidget(parent)
{
//ui
m_layout = new QVBoxLayout(this);
m_valueSlider_1 = new QSlider();
m_valueSlider_2 = new QSlider();
m_sumSlider = new QSlider();
m_addBtn = new QPushButton("Add");
m_valueSlider_1->setOrientation(Qt::Horizontal);
m_valueSlider_2->setOrientation(Qt::Horizontal);
m_sumSlider->setOrientation(Qt::Horizontal);
m_layout->addWidget(m_valueSlider_1);
m_layout->addWidget(m_valueSlider_2);
m_layout->addWidget(m_sumSlider);
m_layout->addWidget(m_addBtn);
this->resize(300, 200);
//逻辑
connect(m_addBtn, SIGNAL(clicked(bool)), this, SLOT(slot_addBtn(bool)));
}
View::~View()
{
}
void View::slot_addBtn(bool)
{
int v1 = m_valueSlider_1->value();
int v2 = m_valueSlider_2->value();
emit sig_addFun(v1, v2);
}
void View::slot_sumUpdate(int sum)
{
m_sumSlider->setValue(sum);
}
- controller.h(未修改)
- controller.cpp(未修改)
- main.cpp(未修改)
5. 对MVC和MVP的理解
5.1 MVC
- 网上对
MVC
的解释有很多,比较混乱 - 我下面是我比较认可的一种:
Controller
作为View
和Model
之间的中介传递信息View
与Model
彻底解耦Model
开发者与View
开发者可以互相独立开发

5.2 MVP
MVP
的概念更混乱。- 维基百科上解释
MVP
是MVC
的改进。 - 暂且将MVP理解为一种特殊的
MCV
吧。
参考
# 浅谈 MVC、MVP 和 MVVM 架构模式\]([浅谈 MVC、MVP 和 MVVM 架构模式 - 面向信仰编程](https://link.juejin.cn?target=https%3A%2F%2Fdraven.co%2Fmvx%2F "https://draven.co/mvx/"))