背景
通常在一些上位机的桌面软件开发中,界面会有很多操作控件,实现界面和底层业务逻辑的分离是非常有必要的,这样的代码层次分明,界面和业务逻辑耦合度低。
架构的组成
架构大致分为两个部分:
- 一个是指令
- 一个是指令执行管理器
界面在控件在进行某些操作后,创建相应的指令对象,然后将指令对象添加到指令执行管理器中(入队列),然后由指令执行管理器中的线程进行统一处理,执行管理器内部有一个队列容器,执行一条指令,出队列一条指令。
架构代码
- 指令基类 (头文件)
#ifndef IUICOMMAND_H
#define IUICOMMAND_H
#include <QObject>
class CmdExecuteMgr;
class IUICommand : public QObject
{
Q_OBJECT
public:
explicit IUICommand(bool bReply = false, QObject *parent = nullptr);
virtual ~IUICommand();
public:
void AddToCmdExecuteMgr(CmdExecuteMgr* pCmdExeMgr); // 将指令添加到指令执行管理容器中
virtual void Execute() = 0; // 执行指令,执行的返回成功或失败结果返给 m_bExeResult
bool GetReply();
bool GetExeResult();
void EmitSignalCmdExecuteOver();
signals:
void SignalCmdExecuteOver(IUICommand*); // 指令执行完成的信号
public slots:
protected:
bool m_bReply;
bool m_bExeResult;
};
#endif // IUICOMMAND_H
- 指令基类 (源文件)
#include "IUICommand.h"
#include "CmdExecuteMgr.h"
IUICommand::IUICommand(bool bReply /*= false*/, QObject *parent /*= nullptr*/)
: QObject(parent), m_bReply(bReply), m_bExeResult(false)
{
// ....
}
IUICommand::~IUICommand()
{
}
void IUICommand::AddToCmdExecuteMgr(CmdExecuteMgr* pCmdExeMgr)
{
if (nullptr != pCmdExeMgr)
pCmdExeMgr->AppendCmd(this);
}
bool IUICommand::GetReply()
{
return m_bReply;
}
bool IUICommand::GetExeResult()
{
return m_bExeResult;
}
void IUICommand::EmitSignalCmdExecuteOver()
{
emit SignalCmdExecuteOver(this);
}
- 命令执行管理器(头文件)
#ifndef CMDEXECUTEMGR_H
#define CMDEXECUTEMGR_H
#include <list>
#include <QThread>
#include "IUICommand.h"
using std::list;
class CmdExecuteMgr : public QThread
{
Q_OBJECT
public:
explicit CmdExecuteMgr(QObject *parent = nullptr);
~CmdExecuteMgr() override;
public:
void AppendCmd(IUICommand* pCmd);
protected:
virtual void run() override;
private:
void HandleNextCmd();
signals:
public slots:
private:
list<IUICommand*> m_CmdList;
};
#endif // CMDEXECUTEMGR_H
- 命令执行管理器(源文件)
#include "CmdExecuteMgr.h"
CmdExecuteMgr::CmdExecuteMgr(QObject *parent) : QThread(parent)
{
}
CmdExecuteMgr::~CmdExecuteMgr()
{
}
void CmdExecuteMgr::AppendCmd(IUICommand* pCmd)
{
if (nullptr != pCmd)
m_CmdList.push_back(pCmd);
}
void CmdExecuteMgr::run()
{
// 这里在正式的项目中一般要设置为软件运行的标识符
while (true)
{
HandleNextCmd();
// 这个时间可以自主设置
QThread::msleep(100);
}
}
void CmdExecuteMgr::HandleNextCmd()
{
if (m_CmdList.empty())
return;
IUICommand* pCmd = m_CmdList.front();
if (nullptr == pCmd)
return;
pCmd->Execute();
// 需要返回
if (pCmd->GetReply())
{
pCmd->EmitSignalCmdExecuteOver();
}
else
{
delete pCmd;
}
m_CmdList.pop_front();
}
示例:
- 打开文件指令(头文件)
#ifndef OPENFILECMD_H
#define OPENFILECMD_H
#include <QObject>
#include <iostream>
#include <string>
#include "IUICommand.h"
using namespace std;
struct FileData
{
};
class OpenFileCmd : public IUICommand
{
Q_OBJECT
public:
explicit OpenFileCmd(const string& strFilePath, bool bReply = false);
~OpenFileCmd() override;
public:
void SetFilePath(const string& strFilePath);
const string& GetFilePath() const;
const FileData& GetFileData() const;
void Execute() override;
signals:
public slots:
private:
string m_strFilePath; // 指令执行需要的参数
FileData m_FileData;
};
#endif // OPENFILECMD_H
- 打开文件指令(源文件)
#include "OpenFileCmd.h"
OpenFileCmd::OpenFileCmd(const string& strFilePath, bool bReply /*= false*/)
: IUICommand(bReply), m_strFilePath(strFilePath)
{
// ...
}
OpenFileCmd::~OpenFileCmd()
{
}
void OpenFileCmd::SetFilePath(const string& strFilePath)
{
m_strFilePath = strFilePath;
}
const string& OpenFileCmd::GetFilePath() const
{
return m_strFilePath;
}
const FileData &OpenFileCmd::GetFileData() const
{
return m_FileData;
}
void OpenFileCmd::Execute()
{
// 打开文件
// 将数据写到 m_FileData
// 将打开的结果给到 m_bExeResult
}