Qt中MVP架构与抽象DAO层的结合示例

前言

在之前的开发中,对于业务的解耦都是单独抽取出一个类,并且大量的业务逻辑写在ui类中,在学习了MVP架构模式后,尝试实现通过MVP(Model-View-Presenter)架构模式与抽象DAO(Data Access Object)层的结合,编写一个示例项目进行学习,具体的学习项目已上传git,地址:https://gitee.com/zbylalalala1/mvp_-study-demo.git。

一、MVP架构模式简介

MVP架构将应用程序分为三个核心组件:

  • Model :负责业务逻辑和数据管理
  • View :负责用户界面的展示和用户交互
  • Presenter :作为Model和View之间的桥梁,处理业务逻辑并协调两者之间的通信
    相比传统的MVC模式,MVP模式中的View更加被动,所有的业务逻辑都由Presenter处理,这使得单元测试变得更加容易。

二、整体架构设计

系统在传统MVP基础上增加了抽象DAO层,形成了如下的分层架构:

┌─────────────────┐
│      View       │ 用户界面层
├─────────────────┤
│   Presenter     │ 业务逻辑层
├─────────────────┤
│     Model       │ 业务模型层
├─────────────────┤
│  IDeviceDAO     │ 数据访问抽象层
├─────────────────┤
│ SqliteDeviceDAO │ 具体实现层
├─────────────────┤
│DatabaseManager  │ 数据库管理层
└─────────────────┘

三、核心架构组件分析

3.1 抽象DAO层设计

抽象DAO层通过接口隔离了具体的数据访问实现:

// 抽象设备DAO接口
class IDeviceDAO : public QObject {
    Q_OBJECT

public:
    virtual ~IDeviceDAO() = default;
    
    // 基本CRUD操作
    virtual bool addDevice(const Device& 
    device) = 0;
    virtual bool updateDevice(const Device& 
    device) = 0;
    virtual bool deleteDevice(int id) = 0;
    virtual Device getDeviceById(int id) = 0;
    
    // 查询操作
    virtual QList<Device> getAllDevices() = 0;
    virtual QList<Device> searchDevices(const 
    QString& searchText) = 0;
    virtual QList<Device> getDevicesByPage
    (int page, int pageSize, const QString& 
    filter = "") = 0;
    
    // 统计操作
    virtual int getTotalCount(const QString& 
    filter = "") = 0;
    
    // 初始化数据
    virtual bool insertTestData() = 0;
    virtual bool hasData() = 0;
    
signals:
    void errorOccurred(const QString &error);
};

// SQLite具体实现类
class SqliteDeviceDAO : public IDeviceDAO {
    Q_OBJECT
public:
    explicit SqliteDeviceDAO(QObject *parent 
    = nullptr);
    
    // 实现接口方法...
    
private:
    DatabaseManager* m_dbManager;
    Device queryToDevice(const QSqlQuery& 
    query);
};

3.2 Model层设计

Model层通过依赖倒置原则,依赖于抽象的IDeviceDAO接口而非具体实现:

class DeviceModel : public QObject {
    Q_OBJECT

public:
    explicit DeviceModel(QObject *parent = 
    nullptr);
    ~DeviceModel();
    
    bool initialize();
    QSqlQueryModel* getTableModel();
    
    // 设备操作
    bool addDevice(const Device& device);
    bool updateDevice(const Device& device);
    bool deleteDevice(int id);
    
    // 搜索和过滤
    void searchDevices(const QString& 
    searchText);
    void resetFilter();
    
    // 分页控制
    void setPageSize(int pageSize);
    void setCurrentPage(int page);
    int getTotalPages() const;
    
signals:
    void dataChanged();
    void errorOccurred(const QString &error);

private:
    IDeviceDAO* m_deviceDAO;  // 使用接口指针,
    实现依赖倒置
    DatabaseManager* m_dbManager;
    QSqlQueryModel* m_tableModel;
    
    int m_pageSize;
    int m_currentPage;
    int m_totalRecords;
    QString m_currentFilter;
    
    void refreshData();
    void updatePagination();
};

3.3 Presenter层设计

Presenter作为View和Model之间的协调者,实现了完全的事件驱动架构:

class DevicePresenter : public QObject {
    Q_OBJECT

public:
    explicit DevicePresenter(QObject *parent 
    = nullptr);
    ~DevicePresenter();
    
    // 依赖注入接口
    void setModel(DeviceModel* model);
    void setView(DeviceView* view);
    void connectSignals();
    bool initialize();

private slots:
    // 处理View的信号
    void onSearchRequested(const QString& 
    searchText);
    void onAddDeviceRequested();
    void onEditDeviceRequested(int deviceId);
    void onDeleteDeviceRequested(int 
    deviceId);
    
    // 处理Model的信号
    void onModelDataChanged();
    void onModelErrorOccurred(const QString& 
    error);

private:
    DeviceModel* m_model;
    DeviceView* m_view;
    bool m_initialized;
};

3.4 View层设计

View层通过信号-槽机制与Presenter通信,保持完全的被动性:

class DeviceView : public QWidget {
    Q_OBJECT

public:
    explicit DeviceView(QWidget *parent = 
    nullptr);
    
    // 界面更新接口
    void setTableModel(QAbstractItemModel* 
    model);
    void updatePaginationInfo(int 
    currentPage, int totalPages, int 
    totalRecords, int pageSize);
    
    // 用户输入接口
    QString getSearchText() const;
    int getSelectedPageSize() const;

signals:
    // 用户操作信号
    void searchRequested(const QString& 
    searchText);
    void addDeviceRequested();
    void editDeviceRequested(int deviceId);
    void deleteDeviceRequested(int deviceId);
    void pageChanged(int page);
    void pageSizeChanged(int pageSize);

public slots:
    void showMessage(const QString& message);
    void showError(const QString& error);
};

四、依赖注入与生命周期管理

在main函数中,我们通过依赖注入的方式创建和管理各个组件:

int main(int argc, char *argv[]) {
    QApplication app(argc, argv);
    
    // 创建Model
    DeviceModel* model = new DeviceModel(&
    app);
    
    // 初始化Model
    if (!model->initialize()) {
        QMessageBox::critical(nullptr, "错误
        ", "无法初始化设备管理系统!");
        return -1;
    }
    
    // 创建View
    DeviceView* view = new DeviceView();
    
    // 创建Presenter
    DevicePresenter* presenter = new 
    DevicePresenter(&app);
    
    // 依赖注入
    presenter->setModel(model);
    presenter->setView(view);
    presenter->connectSignals();
    
    // 初始化并启动
    if (!presenter->initialize()) {
        return -1;
    }
    
    view->show();
    return app.exec();
}
posted @ 2025-08-01 09:10  来一碗糖醋锦鲤  阅读(59)  评论(0)    收藏  举报