一、什么是 MVC?
MVC = Model(模型) + View(视图) + Controller(控制器)
它是一种非常经典的软件架构模式,用来实现:
- 界面与业务逻辑解耦
- 数据与 UI 分离
- 便于扩展、维护、单元测试
二、MVC 各部分的职责
Model(模型)
负责数据和业务逻辑,包括:
- 数据结构(类、结构体)
- 数据处理、计算
- 数据读写(数据库、网络等)
- 状态管理
Model 不关心界面如何展示数据。
View(视图)
负责显示数据(UI 部分),通常包括:
- 一切界面元素(按钮、表格、文本、控件)
- 与用户的交互(点击、输入等)
- 视觉样式
View 不包含业务逻辑。
Controller(控制器)
负责协调 Model 和 View 之间的交互。
- 接收 View 的事件(点击按钮、选择项等)
- 调用 Model 完成数据处理
- 将 Model 的结果更新到 View
三、MVC 数据流
典型流程:
用户操作 View(UI)
↓
Controller 接管事件
↓
Controller 调用 Model 修改数据
↓
Model 处理数据后通知 Controller / View
↓
View 根据结果刷新界面
MVC 架构图(简化)
+-----------+ +-------------+ +---------+
| View | <---> | Controller | <---> | Model |
+-----------+ +-------------+ +---------+
↑ |
|________________________ 数据变化通知 __________|
四、完整示例
加法/减法/乘法/除法计算器的 MVC 示例
- 不暴露成员变量
- 控制器不直接操作 UI
- Model 使用私有成员 + getter/setter
- View 只负责显示,不参与计算
- Controller 完整隔离业务逻辑
整个代码结构如下:
CalculatorModel —— 数据与业务逻辑(计算)
CalculatorView —— UI 层
CalculatorController —— 事件与逻辑调度
1.CalculatorView(UI 层,只负责输入与输出)
// CalculatorView.h
#pragma once
#include <QWidget>
QT_BEGIN_NAMESPACE
class QLineEdit;
class QPushButton;
QT_END_NAMESPACE
class CalculatorView : public QWidget
{
Q_OBJECT
public:
explicit CalculatorView(QWidget* parent=nullptr);
double operandA() const;
double operandB() const;
void setResult(double value);
signals:
void addRequested();
void subRequested();
void mulRequested();
void divRequested();
private:
QLineEdit* m_editA;
QLineEdit* m_editB;
QLineEdit* m_editResult;
QPushButton* m_btnAdd;
QPushButton* m_btnSub;
QPushButton* m_btnMul;
QPushButton* m_btnDiv;
};
// CalculatorView.cpp
#include "CalculatorView.h"
#include <QGridLayout>
#include <QLineEdit>
#include <QPushButton>
CalculatorView::CalculatorView(QWidget* parent)
: QWidget(parent)
{
m_editA = new QLineEdit(this);
m_editB = new QLineEdit(this);
m_editResult = new QLineEdit(this);
m_editResult->setReadOnly(true);
m_btnAdd = new QPushButton("+", this);
m_btnSub = new QPushButton("-", this);
m_btnMul = new QPushButton("*", this);
m_btnDiv = new QPushButton("/", this);
auto layout = new QGridLayout(this);
layout->addWidget(m_editA, 0, 0, 1, 2);
layout->addWidget(m_editB, 1, 0, 1, 2);
layout->addWidget(m_editResult, 2, 0, 1, 2);
layout->addWidget(m_btnAdd, 3, 0);
layout->addWidget(m_btnSub, 3, 1);
layout->addWidget(m_btnMul, 4, 0);
layout->addWidget(m_btnDiv, 4, 1);
connect(m_btnAdd, &QPushButton::clicked, this, &CalculatorView::addRequested);
connect(m_btnSub, &QPushButton::clicked, this, &CalculatorView::subRequested);
connect(m_btnMul, &QPushButton::clicked, this, &CalculatorView::mulRequested);
connect(m_btnDiv, &QPushButton::clicked, this, &CalculatorView::divRequested);
}
double CalculatorView::operandA() const
{
return m_editA->text().toDouble();
}
double CalculatorView::operandB() const
{
return m_editB->text().toDouble();
}
void CalculatorView::setResult(double value)
{
m_editResult->setText(QString::number(value));
}
2. CalculatorModel(数据模型,真正的计算实现)
// CalculatorModel.h
#pragma once
#include <QObject>
class CalculatorModel : public QObject
{
Q_OBJECT
public:
explicit CalculatorModel(QObject* parent=nullptr);
void setOperands(double a, double b);
double operandA() const;
double operandB() const;
double add() const;
double sub() const;
double mul() const;
double div(bool& ok) const;
private:
double m_a = 0;
double m_b = 0;
};
// CalculatorModel.cpp
#include "CalculatorModel.h"
CalculatorModel::CalculatorModel(QObject* parent)
: QObject(parent)
{
}
void CalculatorModel::setOperands(double a, double b)
{
m_a = a;
m_b = b;
}
double CalculatorModel::operandA() const { return m_a; }
double CalculatorModel::operandB() const { return m_b; }
double CalculatorModel::add() const { return m_a + m_b; }
double CalculatorModel::sub() const { return m_a - m_b; }
double CalculatorModel::mul() const { return m_a * m_b; }
double CalculatorModel::div(bool& ok) const
{
if (m_b == 0) {
ok = false;
return 0;
}
ok = true;
return m_a / m_b;
}
3. CalculatorController(逻辑调度层,MVC 核心)
// CalculatorController.h
#pragma once
#include <QObject>
class CalculatorModel;
class CalculatorView;
class CalculatorController : public QObject
{
Q_OBJECT
public:
CalculatorController(CalculatorModel* model,
CalculatorView* view,
QObject* parent=nullptr);
private slots:
void onAdd();
void onSub();
void onMul();
void onDiv();
private:
CalculatorModel* m_model;
CalculatorView* m_view;
};
// CalculatorController.cpp
#include "CalculatorController.h"
#include "CalculatorModel.h"
#include "CalculatorView.h"
CalculatorController::CalculatorController(
CalculatorModel* model,
CalculatorView* view,
QObject* parent)
: QObject(parent)
, m_model(model)
, m_view(view)
{
connect(m_view, &CalculatorView::addRequested, this, &CalculatorController::onAdd);
connect(m_view, &CalculatorView::subRequested, this, &CalculatorController::onSub);
connect(m_view, &CalculatorView::mulRequested, this, &CalculatorController::onMul);
connect(m_view, &CalculatorView::divRequested, this, &CalculatorController::onDiv);
}
void CalculatorController::onAdd()
{
m_model->setOperands(m_view->operandA(), m_view->operandB());
m_view->setResult(m_model->add());
}
void CalculatorController::onSub()
{
m_model->setOperands(m_view->operandA(), m_view->operandB());
m_view->setResult(m_model->sub());
}
void CalculatorController::onMul()
{
m_model->setOperands(m_view->operandA(), m_view->operandB());
m_view->setResult(m_model->mul());
}
void CalculatorController::onDiv()
{
m_model->setOperands(m_view->operandA(), m_view->operandB());
bool ok = true;
double r = m_model->div(ok);
if (!ok)
m_view->setResult(0), qWarning("除数不能为0");
else
m_view->setResult(r);
}
调用:
#include <QApplication>
#include "calculator_model.h"
#include "calculator_view.h"
#include "calculator_controller.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
CalculatorView view;
CalculatorModel model;
CalculatorController(&model, &view);
view.show();
return a.exec();
}
Signal + Slot 版的 MVC 架构
- Controller → Model 全靠 signal/slot,不直接调用函数
- Model → View 更新结果也靠 signal/slot
- Controller 不暴露成员,Model 不暴露内部变量
- View 只发送 UI 的操作,不做业务逻辑
1. Model(数据 + 计算逻辑)
CalculatorModel.h
#pragma once
#include <QObject>
class CalculatorModel : public QObject
{
Q_OBJECT
public:
explicit CalculatorModel(QObject *parent = nullptr);
signals:
void resultChanged(double result);
public slots:
void onAdd(double a, double b);
void onSub(double a, double b);
void onMul(double a, double b);
void onDiv(double a, double b);
};
CalculatorModel.cpp
#include "CalculatorModel.h"
CalculatorModel::CalculatorModel(QObject *parent)
: QObject(parent)
{
}
void CalculatorModel::onAdd(double a, double b)
{
emit resultChanged(a + b);
}
void CalculatorModel::onSub(double a, double b)
{
emit resultChanged(a - b);
}
void CalculatorModel::onMul(double a, double b)
{
emit resultChanged(a * b);
}
void CalculatorModel::onDiv(double a, double b)
{
if (b == 0)
{
emit resultChanged(std::numeric_limits<double>::infinity());
return;
}
emit resultChanged(a / b);
}
2. Controller(纯转发:View → Model)
CalculatorController.h
#pragma once
#include <QObject>
class CalculatorModel;
class CalculatorController : public QObject
{
Q_OBJECT
public:
explicit CalculatorController(CalculatorModel *model, QObject *parent = nullptr);
signals:
// Controller 接收 View 的请求(UI 发来指令)
void addRequested(double a, double b);
void subRequested(double a, double b);
void mulRequested(double a, double b);
void divRequested(double a, double b);
private:
CalculatorModel *m_model;
};
CalculatorController.cpp
#include "CalculatorController.h"
#include "CalculatorModel.h"
CalculatorController::CalculatorController(CalculatorModel *model, QObject *parent)
: QObject(parent), m_model(model)
{
// Controller 的请求信号 → Model 的槽函数
connect(this, &CalculatorController::addRequested, m_model, &CalculatorModel::onAdd);
connect(this, &CalculatorController::subRequested, m_model, &CalculatorModel::onSub);
connect(this, &CalculatorController::mulRequested, m_model, &CalculatorModel::onMul);
connect(this, &CalculatorController::divRequested, m_model, &CalculatorModel::onDiv);
}
3. View(UI 触发操作 + 显示 Model 的结果)
CalculatorView.h
#pragma once
#include <QWidget>
class CalculatorController;
class CalculatorView : public QWidget
{
Q_OBJECT
public:
explicit CalculatorView(CalculatorController *controller, QWidget *parent = nullptr);
signals:
void calculateAdd(double a, double b);
void calculateSub(double a, double b);
void calculateMul(double a, double b);
void calculateDiv(double a, double b);
public slots:
void onResultChanged(double result);
private:
CalculatorController *m_controller;
QLineEdit *m_editA;
QLineEdit *m_editB;
QLabel *m_labelResult;
};
CalculatorView.cpp
#include "CalculatorView.h"
#include "CalculatorController.h"
#include <QPushButton>
#include <QLineEdit>
#include <QLabel>
#include <QVBoxLayout>
CalculatorView::CalculatorView(CalculatorController *controller, QWidget *parent)
: QWidget(parent), m_controller(controller)
{
m_editA = new QLineEdit(this);
m_editB = new QLineEdit(this);
m_labelResult = new QLabel("结果:", this);
auto btnAdd = new QPushButton("加法", this);
auto btnSub = new QPushButton("减法", this);
auto btnMul = new QPushButton("乘法", this);
auto btnDiv = new QPushButton("除法", this);
auto layout = new QVBoxLayout(this);
layout->addWidget(m_editA);
layout->addWidget(m_editB);
layout->addWidget(btnAdd);
layout->addWidget(btnSub);
layout->addWidget(btnMul);
layout->addWidget(btnDiv);
layout->addWidget(m_labelResult);
// View 的信号 → Controller
connect(btnAdd, &QPushButton::clicked, this, [=](){
emit m_controller->addRequested(m_editA->text().toDouble(), m_editB->text().toDouble());
});
connect(btnSub, &QPushButton::clicked, this, [=](){
emit m_controller->subRequested(m_editA->text().toDouble(), m_editB->text().toDouble());
});
connect(btnMul, &QPushButton::clicked, this, [=](){
emit m_controller->mulRequested(m_editA->text().toDouble(), m_editB->text().toDouble());
});
connect(btnDiv, &QPushButton::clicked, this, [=](){
emit m_controller->divRequested(m_editA->text().toDouble(), m_editB->text().toDouble());
});
}
void CalculatorView::onResultChanged(double result)
{
m_labelResult->setText(QString("结果:%1").arg(result));
}
4. main.cpp:连接 View ← Model
#include <QApplication>
#include "CalculatorModel.h"
#include "CalculatorController.h"
#include "CalculatorView.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
CalculatorModel model;
CalculatorController controller(&model);
CalculatorView view(&controller);
// Model → View
QObject::connect(&model, &CalculatorModel::resultChanged,
&view, &CalculatorView::onResultChanged);
view.show();
return a.exec();
}
加/减/乘/除计算器的 MVC + Q_PROPERTY 示例
1.Model(业务逻辑,不直接暴露 UI,但用 Q_PROPERTY 让结果可观察)
#ifndef CALCULATORMODEL_H
#define CALCULATORMODEL_H
#include <QObject>
class CalculatorModel : public QObject
{
Q_OBJECT
Q_PROPERTY(double result READ result NOTIFY resultChanged)
public:
explicit CalculatorModel(QObject *parent = nullptr) : QObject(parent) {}
double result() const { return m_result; }
public slots:
void add(double a, double b) { setResult(a + b); }
void sub(double a, double b) { setResult(a - b); }
void mul(double a, double b) { setResult(a * b); }
void div(double a, double b) {
if(b == 0) setResult(0);
else setResult(a / b);
}
signals:
void resultChanged();
private:
void setResult(double value) {
if(m_result != value) {
m_result = value;
emit resultChanged();
}
}
double m_result = 0;
};
#endif // CALCULATORMODEL_H
2.Controller(纯转发 View → Model)
#ifndef CALCULATORCONTROLLER_H
#define CALCULATORCONTROLLER_H
#include <QObject>
#include "calculator_model.h"
#include "calculator_view.h"
class CalculatorController : public QObject
{
Q_OBJECT
public:
explicit CalculatorController(CalculatorModel* model, CalculatorView* view, QObject* parent = nullptr)
: QObject(parent), m_model(model), m_view(view) {
// View → Controller
connect(m_view, &CalculatorView::addRequested, this, &CalculatorController::handleAdd);
connect(m_view, &CalculatorView::subRequested, this, &CalculatorController::handleSub);
connect(m_view, &CalculatorView::mulRequested, this, &CalculatorController::handleMul);
connect(m_view, &CalculatorView::divRequested, this, &CalculatorController::handleDiv);
// Model (Q_PROPERTY result) → View
connect(m_model, &CalculatorModel::resultChanged, [&](){
m_view->updateResult(m_model->result());
});
}
// 接收 View 信号 → 转发到 Model
public slots:
void handleAdd(double a, double b) { m_model->add(a, b); }
void handleSub(double a, double b) { m_model->sub(a, b); }
void handleMul(double a, double b) { m_model->mul(a, b); }
void handleDiv(double a, double b) { m_model->div(a, b); }
private:
CalculatorModel* m_model;
CalculatorView* m_view;
};
#endif // CALCULATORCONTROLLER_H
3.View(UI 完全不做计算,只发送信号和显示结果)
ifndef CALCULATORVIEW_H
#define CALCULATORVIEW_H
#include <QWidget>
class QLineEdit;
class QPushButton;
class QLabel;
class CalculatorView : public QWidget
{
Q_OBJECT
public:
explicit CalculatorView(QWidget* parent = nullptr);
signals:
void addRequested(double a, double b);
void subRequested(double a, double b);
void mulRequested(double a, double b);
void divRequested(double a, double b);
public slots:
void updateResult(double r);
private:
QLineEdit* m_editA;
QLineEdit* m_editB;
QLabel* m_labelResult;
};
#endif // CALCULATORVIEW_H
#include "calculator_view.h"
#include <QLineEdit>
#include <QPushButton>
#include <QLabel>
#include <QGridLayout>
#include <QString>
CalculatorView::CalculatorView(QWidget* parent)
: QWidget(parent)
{
m_editA = new QLineEdit(this);
m_editB = new QLineEdit(this);
m_labelResult = new QLabel("结果: 0", this);
auto btnAdd = new QPushButton("+", this);
auto btnSub = new QPushButton("-", this);
auto btnMul = new QPushButton("*", this);
auto btnDiv = new QPushButton("/", this);
auto layout = new QGridLayout(this);
layout->addWidget(m_editA, 0, 0, 1, 2);
layout->addWidget(m_editB, 1, 0, 1, 2);
layout->addWidget(btnAdd, 2, 0);
layout->addWidget(btnSub, 2, 1);
layout->addWidget(btnMul, 3, 0);
layout->addWidget(btnDiv, 3, 1);
layout->addWidget(m_labelResult, 4, 0, 1, 2);
// 按钮点击 → Controller
connect(btnAdd, &QPushButton::clicked, this, [=](){
emit addRequested(m_editA->text().toDouble(), m_editB->text().toDouble());
});
connect(btnSub, &QPushButton::clicked, this, [=](){
emit subRequested(m_editA->text().toDouble(), m_editB->text().toDouble());
});
connect(btnMul, &QPushButton::clicked, this, [=](){
emit mulRequested(m_editA->text().toDouble(), m_editB->text().toDouble());
});
connect(btnDiv, &QPushButton::clicked, this, [=](){
emit divRequested(m_editA->text().toDouble(), m_editB->text().toDouble());
});
}
void CalculatorView::updateResult(double r)
{
m_labelResult->setText(QString("结果: %1").arg(r));
}
4.main.cpp(连接 MVC + Q_PROPERTY 自动更新)
#include <QApplication>
#include "calculator_model.h"
#include "calculator_controller.h"
#include "calculator_view.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
CalculatorModel model;
CalculatorView view;
CalculatorController controller(&model, &view);
view.show();
return a.exec();
}
MVC + Q_PROPERTY + 双向绑定 的完整计算器示例
特点:
- 支持加、减、乘、除
- 双向绑定:用户在输入框修改操作数 → Model 自动更新
- 结果显示自动更新(Q_PROPERTY + resultChanged)
- MVC 完整分层:Model/Controller/View
1.Model(带 Q_PROPERTY)
#pragma once
#include <QObject>
class CalculatorModel : public QObject
{
Q_OBJECT
Q_PROPERTY(double operandA READ operandA WRITE setOperandA NOTIFY operandAChanged)
Q_PROPERTY(double operandB READ operandB WRITE setOperandB NOTIFY operandBChanged)
Q_PROPERTY(double result READ result NOTIFY resultChanged)
public:
explicit CalculatorModel(QObject* parent = nullptr)
: QObject(parent) {}
double operandA() const { return m_operandA; }
double operandB() const { return m_operandB; }
double result() const { return m_result; }
public slots:
void setOperandA(double v)
{
if(qFuzzyCompare(m_operandA, v)) return;
m_operandA = v;
emit operandAChanged();
}
void setOperandB(double v)
{
if(qFuzzyCompare(m_operandB, v)) return;
m_operandB = v;
emit operandBChanged();
}
void setResult(double v)
{
if(qFuzzyCompare(m_result, v)) return;
m_result = v;
emit resultChanged();
}
signals:
void operandAChanged();
void operandBChanged();
void resultChanged();
private:
double m_operandA = 0;
double m_operandB = 0;
double m_result = 0;
};
2.Controller(转发 View → Model)
#pragma once
#include <QObject>
class CalculatorModel;
class CalculatorController : public QObject
{
Q_OBJECT
public:
explicit CalculatorController(CalculatorModel* model, QObject* parent = nullptr)
: QObject(parent), m_model(model) {}
public slots:
void setOperandA(double v) { m_model->setOperandA(v); }
void setOperandB(double v) { m_model->setOperandB(v); }
void add()
{
m_model->setResult(m_model->operandA() + m_model->operandB());
}
void sub()
{
m_model->setResult(m_model->operandA() - m_model->operandB());
}
void mul()
{
m_model->setResult(m_model->operandA() * m_model->operandB());
}
void div()
{
if (m_model->operandB() == 0)
m_model->setResult(0); // 避免除零崩溃
else
m_model->setResult(m_model->operandA() / m_model->operandB());
}
private:
CalculatorModel* m_model;
};
3.View(UI + 双向绑定)
#pragma once
#include <QWidget>
class QLineEdit;
class QLabel;
class CalculatorModel;
class CalculatorController;
class CalculatorView : public QWidget
{
Q_OBJECT
public:
explicit CalculatorView(CalculatorModel* model,
CalculatorController* controller,
QWidget* parent = nullptr);
private:
QLineEdit* m_editA;
QLineEdit* m_editB;
QLabel* m_labelResult;
CalculatorModel* m_model;
CalculatorController* m_controller;
};
#include "CalculatorView.h"
#include "CalculatorModel.h"
#include "CalculatorController.h"
#include <QLineEdit>
#include <QLabel>
#include <QPushButton>
#include <QGridLayout>
#include <QDoubleValidator>
CalculatorView::CalculatorView(CalculatorModel* model,
CalculatorController* controller,
QWidget* parent)
: QWidget(parent), m_model(model), m_controller(controller)
{
m_editA = new QLineEdit(this);
m_editB = new QLineEdit(this);
m_editA->setValidator(new QDoubleValidator(this));
m_editB->setValidator(new QDoubleValidator(this));
auto btnAdd = new QPushButton("+", this);
auto btnSub = new QPushButton("-", this);
auto btnMul = new QPushButton("*", this);
auto btnDiv = new QPushButton("/", this);
m_labelResult = new QLabel("结果: 0", this);
auto layout = new QGridLayout(this);
layout->addWidget(m_editA, 0, 0, 1, 2);
layout->addWidget(m_editB, 1, 0, 1, 2);
layout->addWidget(btnAdd, 2, 0);
layout->addWidget(btnSub, 2, 1);
layout->addWidget(btnMul, 3, 0);
layout->addWidget(btnDiv, 3, 1);
layout->addWidget(m_labelResult, 4, 0, 1, 2);
// UI -> Controller
connect(m_editA, &QLineEdit::textChanged, this, [=](QString t){
bool ok; double v = t.toDouble(&ok);
if(ok) m_controller->setOperandA(v);
});
connect(m_editB, &QLineEdit::textChanged, this, [=](QString t){
bool ok; double v = t.toDouble(&ok);
if(ok) m_controller->setOperandB(v);
});
// Buttons -> Controller
connect(btnAdd, &QPushButton::clicked, m_controller, &CalculatorController::add);
connect(btnSub, &QPushButton::clicked, m_controller, &CalculatorController::sub);
connect(btnMul, &QPushButton::clicked, m_controller, &CalculatorController::mul);
connect(btnDiv, &QPushButton::clicked, m_controller, &CalculatorController::div);
// Model -> UI(自动更新)
connect(m_model, &CalculatorModel::resultChanged, this, [=](){
m_labelResult->setText(QString("结果:%1").arg(m_model->result()));
});
}
4.main.cpp(连接 MVC)
#include <QApplication>
#include "CalculatorModel.h"
#include "CalculatorController.h"
#include "CalculatorView.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
CalculatorModel model;
CalculatorController controller(&model);
CalculatorView view(&model, &controller);
view.show();
return a.exec();
}

1627

被折叠的 条评论
为什么被折叠?



