Qt中MVP架構(gòu)與抽象DAO層的結(jié)合示例
前言
在之前的開發(fā)中,對于業(yè)務的解耦都是單獨抽取出一個類,并且大量的業(yè)務邏輯寫在ui類中,在學習了MVP架構(gòu)模式后,嘗試實現(xiàn)通過MVP(Model-View-Presenter)架構(gòu)模式與抽象DAO(Data Access Object)層的結(jié)合,編寫一個示例項目進行學習,具體的學習項目已上傳git,地址:https://gitee.com/zbylalalala1/mvp_-study-demo.git。
一、MVP架構(gòu)模式簡介
MVP架構(gòu)將應用程序分為三個核心組件:
- Model :負責業(yè)務邏輯和數(shù)據(jù)管理
- View :負責用戶界面的展示和用戶交互
- Presenter :作為Model和View之間的橋梁,處理業(yè)務邏輯并協(xié)調(diào)兩者之間的通信
相比傳統(tǒng)的MVC模式,MVP模式中的View更加被動,所有的業(yè)務邏輯都由Presenter處理,這使得單元測試變得更加容易。
二、整體架構(gòu)設計
系統(tǒng)在傳統(tǒng)MVP基礎(chǔ)上增加了抽象DAO層,形成了如下的分層架構(gòu):
┌─────────────────┐
│ View │ 用戶界面層
├─────────────────┤
│ Presenter │ 業(yè)務邏輯層
├─────────────────┤
│ Model │ 業(yè)務模型層
├─────────────────┤
│ IDeviceDAO │ 數(shù)據(jù)訪問抽象層
├─────────────────┤
│ SqliteDeviceDAO │ 具體實現(xiàn)層
├─────────────────┤
│DatabaseManager │ 數(shù)據(jù)庫管理層
└─────────────────┘
三、核心架構(gòu)組件分析
3.1 抽象DAO層設計
抽象DAO層通過接口隔離了具體的數(shù)據(jù)訪問實現(xiàn):
// 抽象設備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;
// 統(tǒng)計操作
virtual int getTotalCount(const QString&
filter = "") = 0;
// 初始化數(shù)據(jù)
virtual bool insertTestData() = 0;
virtual bool hasData() = 0;
signals:
void errorOccurred(const QString &error);
};
// SQLite具體實現(xiàn)類
class SqliteDeviceDAO : public IDeviceDAO {
Q_OBJECT
public:
explicit SqliteDeviceDAO(QObject *parent
= nullptr);
// 實現(xiàn)接口方法...
private:
DatabaseManager* m_dbManager;
Device queryToDevice(const QSqlQuery&
query);
};
3.2 Model層設計
Model層通過依賴倒置原則,依賴于抽象的IDeviceDAO接口而非具體實現(xiàn):
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; // 使用接口指針,
實現(xiàn)依賴倒置
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之間的協(xié)調(diào)者,實現(xiàn)了完全的事件驅(qū)動架構(gòu):
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函數(shù)中,我們通過依賴注入的方式創(chuàng)建和管理各個組件:
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
// 創(chuàng)建Model
DeviceModel* model = new DeviceModel(&
app);
// 初始化Model
if (!model->initialize()) {
QMessageBox::critical(nullptr, "錯誤
", "無法初始化設備管理系統(tǒng)!");
return -1;
}
// 創(chuàng)建View
DeviceView* view = new DeviceView();
// 創(chuàng)建Presenter
DevicePresenter* presenter = new
DevicePresenter(&app);
// 依賴注入
presenter->setModel(model);
presenter->setView(view);
presenter->connectSignals();
// 初始化并啟動
if (!presenter->initialize()) {
return -1;
}
view->show();
return app.exec();
}
浙公網(wǎng)安備 33010602011771號