MVC架构的介绍和使用

一、什么是 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();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值