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();
}
Qt中MVP架构与抽象DAO层的结合示例的更多相关文章
- MVP架构在xamarin android中的简单使用
好几个月没写文章了,使用xamarin android也快接近两年,还有一个月职业生涯就到两个年了,从刚出来啥也不会了,到现在回头看这个项目,真jb操蛋(真辛苦了实施的人了,无数次吐槽怎么这么丑),怪 ...
- (转)淘淘商城系列——SSM框架整合之Dao层整合
http://blog.csdn.net/yerenyuan_pku/article/details/72721093 一个项目中往往有三层即Dao层.Service层和Web层,看标题就知道了,本文 ...
- 02.MyBatis在DAO层开发使用的Mapper动态代理方式
在实际开发中,Mybatis作用于DAO层,那么Service层该如何调用Mybatis Mybatis鼓励使用Mapper动态代理的方式 Mapper接口开发方法只需要程序员编写Mapper接口(相 ...
- 一个项目中说系统分为表现层、控制层、逻辑层、DAO层和最终数据库五层架构-转
表现层就是看到的东西,比如你现在看到的当前页面控制层就将你的请求从页面传到后台代码逻辑层就是处理你的请求的代码DAO层就是将数据存到数据库中的代码数据库就是数据库了,存东西用的 ,DAO层就是将访问数 ...
- 转:Android开发中的MVP架构(最后链接资源不错)
Android开发中的MVP架构 最近越来越多的人开始谈论架构.我周围的同事和工程师也是如此.尽管我还不是特别深入理解MVP和DDD,但是我们的新项目还是决定通过MVP来构建. 这篇文章是我通过研究和 ...
- 转: Android开发中的MVP架构详解(附加链接比较不错)
转: http://www.codeceo.com/article/android-mvp-artch.html 最近越来越多的人开始谈论架构.我周围的同事和工程师也是如此.尽管我还不是特别深入理解M ...
- 设计模式笔记之二:Android开发中的MVP架构(转)
写在前面,本博客来源于公众号文章:http://mp.weixin.qq.com/s?__biz=MzA3MDMyMjkzNg==&mid=402435540&idx=1&sn ...
- Android中的MVP架构分解和实现
1.概述 传统的Android开发架构通常是MVC模式. Model:业务逻辑和实体模型 View:相应于布局文件 Controllor:相应于Activity 单独从逻辑看起来很好,与我们做Web开 ...
- Android中的MVP架构初探
说来羞愧,MVP的架构模式已经在Android领域出现一两年了.可是到今天自己才開始Android领域中的MVP架构征程. 闲话不多说,開始吧. 一.架构演变概述 我记得我找第一份工作时,面试官问我& ...
- Android架构(一)MVP架构在Android中的实践
Android架构(一)MVP架构在Android中的实践 https://www.300168.com/yidong/show-2790.html 核心提示:为什么要重视程序的架构设计 对程序进 ...
随机推荐
- 一个大对象引起的血案,GC的踩坑实录
背景: 问题: 有个渠道支付服务,负责与所有支付相关服务进行交互,包括 渠道下单支付,渠道成功通知,渠道的对账等 服务4台机,平时跑的都很稳定,通过thrift进行对外提供服务,且平时并未发现访问 ...
- qt获得当前窗口所在屏幕的大小
假如这个窗口的指针为this 记得要加头文件哦 #include <QDesktopWidget> #include <QApplication> //获得当前屏幕是第几屏幕 ...
- obs学习之3——obs初始化、析构
在app的开发过程中,obs相关函数的使用顺序如下: 1.首先,初始化obs // 创建obs if ( !obs_startup( "en-US", nullptr, nullp ...
- Python中的cls变量
技术背景 在Python的类型设计中,有时候会遇到一个cls参数.其实cls参数就是一个约定俗成的名称,用其他的名字也能正常运行但不建议这么用.它的作用类似于实例方法中的self参数,代表的是类本身, ...
- kards卡组推荐——苏美隐蔽
声明:此卡组抗快能力极差,害怕炮兵和小飞机为中后期 隐蔽核心思路: 在第一回合,尽量找杜斯团,找不到如果对方有单位,可以打一个镰刀 第二回合,①有杜斯团:打出隐蔽单位,按隐蔽顺序(附1)出,如果只有1 ...
- 高性能、高扩展、高稳定:解读 EasyMR 大数据组件自定义可扩展能力
随着互联网技术的不断发展以及大数据时代的兴起,企业对于数据分析和洞察的需求日益增长.大多数企业都积累了大量的数据,需要从这些数据中快速灵活地提取有价值的信息,以便为用户提供更好的服务或者帮助企业做出更 ...
- 纯C#软实现openGL(V0.1),黑盒变白盒
纯C#软实现openGL(V0.1),黑盒变白盒 为了彻底掌握openGL,做一个openGL的软实现(命名为SoftGLImpl)是必要的.(而非仅仅调用opengl32.dll) openGL A ...
- oracle 存储过程 for loop 定时任务
记录. 是这么个事,要实现一个需求,当人员表里的数据有更新后需要告知其他系统更新他们自己的人员数据. 我想了一下,表里是有时间戳字段的,那我只要监听这个时间就行,拿到数据后用存储过程把数据插入到中间表 ...
- secp256k1算法详解二(关键理论及源码分析)
1 关键结构体 1.1 secp256k1_fe secp256k1库域元素field element,其具体定义如下 /** This field implementation represents ...
- 重磅消息 | 2025年最新AI+全栈测试开发技能实战指南(第6期)
深度好文,篇幅较长,请耐心看完! 大家好,期盼已久的<全栈测试开发实战训练营>第6期,震撼回归! 1.行业分析 今年是全栈测试开发训练营第6期,本期训练营,首次推出了AI+的模式,所谓的A ...