界面架构- MVP(Qt)

MVP

MVP 架构详解

MVP 架构将传统的 MVC 模式进一步改进,主要目的是让视图(View)尽量"被动",所有业务逻辑都放在 Presenter 中,从而提高代码的测试性和可维护性。其三个核心组件说明如下:

  • Model(模型)

    Model 负责应用的业务逻辑和数据处理。在本示例中,Model 提供一个接口用于计算字符串的长度。Model 与用户界面完全无关,仅关注数据和业务规则。

  • View(视图)

    View 负责构建用户界面,展示数据,并将用户的操作事件(例如按钮点击)传递给 Presenter。为了降低耦合,通常会定义一个 View 接口(例如 IView),而具体的 View(例如 MainWindow)实现这个接口。View 只负责展示,不直接包含业务逻辑。

  • Presenter(表示层/中介者)

    Presenter 作为中介者连接 Model 与 View。它接收来自 View 的用户操作,调用 Model 进行数据处理,再将处理结果反馈给 View 更新显示。Presenter 中的逻辑通常是纯 C++ 类,不依赖于具体的 GUI 组件,因此便于单元测试。

这种架构的优点在于:

  • 关注点分离:业务逻辑与界面展示完全解耦。

  • 提高可测试性:Presenter 逻辑可以独立测试,View 尽可能简单。

  • 便于维护和扩展:每一层职责明确,修改其中一层对其它层影响较小。

示例

示例功能

构建一个简单的应用,界面包含:

  • QLineEdit:供用户输入字符串;
  • QPushButton:当用户点击按钮时,计算 QLineEdit 中文本的字符数;
  • QLabel:显示计算后的字符数。

整体流程:

  1. 用户在 QLineEdit 中输入字符串;
  2. 点击按钮,触发控制器;
  3. 控制器调用模型计算字符串长度;
  4. 控制器将计算结果更新到 QLabel 显示出来。

示例代码

下面给出各个组件的示例代码,代码按照 MVP 的思想分为四个部分:

  • 定义 View 接口
    首先定义一个纯虚接口,规定 View 应实现的更新显示接口。
cpp 复制代码
// IView.h
#ifndef IVIEW_H
#define IVIEW_H

#include <QString>

class IView
{
public:
    virtual ~IView() {}
    // 更新显示结果,例如将计算的长度写入 QLabel
    virtual void updateResult(const QString &result) = 0;
};

#endif // IVIEW_H
  • 定义 Model 类
    Model 只负责业务逻辑,这里只提供计算字符串长度的方法
cpp 复制代码
// Model.h
#ifndef MODEL_H
#define MODEL_H

#include <QString>

class Model
{
public:
    Model() {}

    // 计算并返回字符串的长度
    int getTextLength(const QString &text) {
        return text.length();
    }
};

#endif // MODEL_H
  • 定义 Presenter 类
    Presenter 负责接收 View 传来的请求,调用 Model 处理,再通知 View 更新显示。
cpp 复制代码
// Presenter.h
#ifndef PRESENTER_H
#define PRESENTER_H

#include "IView.h"
#include "Model.h"
#include <QString>

class Presenter
{
public:
    Presenter(IView *view, Model *model)
        : m_view(view), m_model(model) {}

    // 处理按钮点击事件,计算文本长度并通知 View 更新显示
    void onCalculateButtonClicked(const QString &inputText) {
        int length = m_model->getTextLength(inputText);
        m_view->updateResult("长度:" + QString::number(length));
    }

private:
    IView *m_view;
    Model *m_model;
};

#endif // PRESENTER_H
  • 实现具体 View(MainWindow)
    MainWindow 继承自 QWidget 并实现 IView 接口,构建界面,同时将用户操作(按钮点击)转发给 Presenter。
cpp 复制代码
// MainWindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QWidget>
#include "IView.h"

class QLineEdit;
class QPushButton;
class QLabel;
class Presenter;
class Model;

class MainWindow : public QWidget, public IView
{
    Q_OBJECT
public:
    explicit MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

    // IView 接口实现:更新 QLabel 显示计算结果
    void updateResult(const QString &result) override;

private slots:
    // 当按钮点击时,调用 Presenter 处理
    void handleCalculateButton();

private:
    QLineEdit *m_lineEdit;
    QPushButton *m_button;
    QLabel *m_label;

    // MVP 的其它组件
    Presenter *m_presenter;
    Model *m_model;
};

#endif // MAINWINDOW_H
cpp 复制代码
// MainWindow.cpp
#include "MainWindow.h"
#include "Presenter.h"
#include "Model.h"

#include <QLineEdit>
#include <QPushButton>
#include <QLabel>
#include <QVBoxLayout>

MainWindow::MainWindow(QWidget *parent)
    : QWidget(parent)
{
    // 创建控件
    m_lineEdit = new QLineEdit(this);
    m_button = new QPushButton("计算长度", this);
    m_label = new QLabel("结果显示", this);

    // 布局管理
    QVBoxLayout *layout = new QVBoxLayout(this);
    layout->addWidget(m_lineEdit);
    layout->addWidget(m_button);
    layout->addWidget(m_label);
    setLayout(layout);

    // 创建 Model 与 Presenter
    m_model = new Model();
    m_presenter = new Presenter(this, m_model);

    // 连接按钮点击信号到槽函数
    connect(m_button, &QPushButton::clicked, this, &MainWindow::handleCalculateButton);
}

MainWindow::~MainWindow()
{
    delete m_presenter;
    delete m_model;
}

void MainWindow::updateResult(const QString &result)
{
    // 更新 QLabel 显示计算结果
    m_label->setText(result);
}

void MainWindow::handleCalculateButton()
{
    // 获取用户输入
    QString inputText = m_lineEdit->text();
    // 将输入传递给 Presenter 处理
    m_presenter->onCalculateButtonClicked(inputText);
}
  • 主函数入口
    在 main 函数中创建 QApplication 对象并显示 MainWindow。
cpp 复制代码
// main.cpp
#include <QApplication>
#include "MainWindow.h"

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    MainWindow window;
    window.setWindowTitle("Qt MVP 示例");
    window.resize(300, 200);
    window.show();
    return app.exec();
}

总结

在上述 MVP 示例中,我们将应用程序分为三个部分:

  • Model 负责计算字符串的长度,不依赖于任何界面;

  • View(MainWindow)构建用户界面,展示 QLineEdit、按钮和 QLabel,并实现 IView 接口,用于接收 Presenter 的更新通知;

  • Presenter 接收 View 的操作请求,调用 Model 计算结果,再将结果传递回 View 更新界面。

这种设计使得业务逻辑与界面展示彻底分离,便于单元测试 Presenter 以及在项目中对各模块进行独立开发和维护。

相关推荐
yunteng52115 小时前
通用架构(同城双活)(单点接入)
架构·同城双活·单点接入
麦聪聊数据15 小时前
Web 原生架构如何重塑企业级数据库协作流?
数据库·sql·低代码·架构
程序员侠客行16 小时前
Mybatis连接池实现及池化模式
java·后端·架构·mybatis
云中飞鸿16 小时前
QTCreator快捷键
qt
bobuddy18 小时前
射频收发机架构简介
架构·射频工程
十五年专注C++开发18 小时前
QStyleItemDelegate:自定义列表控件类神器
qt·model·view·delegate
桌面运维家18 小时前
vDisk考试环境IO性能怎么优化?VOI架构实战指南
架构
无小道19 小时前
Qt——事件简单介绍
开发语言·前端·qt
一个骇客19 小时前
让你的数据成为“操作日志”和“模型饲料”:事件溯源、CQRS与DataFrame漫谈
架构
鹏北海-RemHusband20 小时前
从零到一:基于 micro-app 的企业级微前端模板完整实现指南
前端·微服务·架构