【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 架构模式 - 面向信仰编程)

相关推荐
用户805533698033 天前
不止三件套:QObject 属性系统全关键字与运行时反射!
c++·qt
xcyxiner3 天前
DicomViewer (vcpkg Windows和ubuntu编译)7
qt
Quz8 天前
QML Hello World 入门示例
qt
xcyxiner11 天前
DicomViewer (dcmtk读取dcm文件)5
qt
xcyxiner12 天前
DicomViewer (后台线程处理文件)4
qt
xcyxiner12 天前
DicomViewer (添加模型类)3
qt
xcyxiner13 天前
DicomViewer (目录调整) 2
qt
xcyxiner13 天前
dcmtk vtk vtk-dicom(gdcm) 编译(debug) v2
qt
桥田智能15 天前
桥田智能 QT-650S:面向白车身焊装的 800kg 重载快换解决方案
开发语言·qt·系统架构
森G15 天前
75、服务器源码解析---------云视频服务项目
linux·服务器·网络·c++·qt