【Qt】Qt中对MVC,MVP的理解

理解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 实现方法

  • ViewModel代码分离,逻辑代码放到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 改进目标

  • ModelView彻底解耦,互相不知道对方的存在。

4.2 实现方法

  • ViewModel中间添加一个中介,即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作为ViewModel之间的中介传递信息
    • ViewModel彻底解耦
    • Model开发者与View开发者可以互相独立开发

5.2 MVP

  • MVP的概念更混乱。
  • 维基百科上解释MVPMVC的改进。
  • 暂且将MVP理解为一种特殊的MCV吧。

参考

# 浅谈 MVC、MVP 和 MVVM 架构模式\]([浅谈 MVC、MVP 和 MVVM 架构模式 - 面向信仰编程](https://link.juejin.cn?target=https%3A%2F%2Fdraven.co%2Fmvx%2F "https://draven.co/mvx/"))

相关推荐
老歌老听老掉牙3 小时前
PyQt5+Qt Designer实战:可视化设计智能参数配置界面,告别手动布局时代!
python·qt
A.A呐5 小时前
【QT第六章】界面优化
开发语言·qt
sycmancia5 小时前
Qt——布局管理器(一)
前端·qt
AlanW6 小时前
QT 信号槽内部实现原理深度解析
qt
A.A呐7 小时前
【QT第五章】系统相关
开发语言·qt
sycmancia7 小时前
Qt——Qt中的标准对话框
开发语言·qt
freshman_y1 天前
Qtcreator怎么新建安卓项目?编写一个五子棋游戏APP?
android·qt
wljy11 天前
Qt入门(一)
开发语言·qt
火山上的企鹅1 天前
QT/QGroundControl 实战:Mission Planner 航线在 QGC 中出现 Takeoff 落到 (0,0) 的排查与修复
qt·mp·qgc·无人机开发
雪的季节1 天前
qt信号槽跨线程使用时候的坑
java·开发语言·qt