多任务响应1
- 1. 架构概述
- 2. 代码示例
- 3. 说明
当系统的一些任务都是同一个对象产生,但需要交由不同对象进行响应。
比如:系统有多个按键,这些按键的共用一个槽函数,但不同的按键对应不同的功能响应。
推荐采用命令模式+分散响应的思想。在这种设计中,
-
每个按键对应一个实现了统一接口(如 ICommand)的命令对象,
-
每个命令负责单一的功能,符合单一职责原则;
-
一个中心的 CommandManager 用于注册和管理命令,根据传入的命令标识执行对应操作;
-
按键的 clicked 信号通过 lambda 表达式与 CommandManager 的执行接口绑定,从而实现分散响应。
下面分为三个部分介绍:
1. 架构概述
设计思想:
-
解耦与扩展性: 每个功能都有独立的命令对象,新增或修改按键响应只需要新增或修改对应命令,而不必改动整体分发逻辑;
-
统一管理: CommandManager 统一管理各个命令对象,与 UI 层解耦。利用 Qt 的信号和槽,UI 层只需负责转发按键事件;
-
灵活性: 利用 C++17 的特性(如 lambda 表达式、std::unique_ptr 等),使代码更加现代化和健壮。
架构示意图:
[UI按钮]
│ clicked()
▼
[Lambda表达式]
│ 调用
▼
[CommandManager] ---- 查找命令 ----> [ICommand] ---> 执行具体功能
2. 代码示例
以下是一个完整的示例代码,包括命令接口、具体命令实现、命令管理器以及如何在 Qt 的主窗口中连接按钮信号。
#include <QApplication>
#include <QWidget>
#include <QPushButton>
#include <QVBoxLayout>
#include <QDebug>
#include <QString>
#include <unordered_map>
#include <memory>
// 1. 定义命令接口
class ICommand {
public:
virtual ~ICommand() = default;
virtual void execute() = 0;
};
// 2. 具体命令实现(示例:启动和停止操作)
class SpindleStartCommand : public ICommand {
public:
void execute() override {
qDebug() << "Spindle started.";
// 此处添加数控软件启动主轴等操作的具体实现
}
};
class SpindleStopCommand : public ICommand {
public:
void execute() override {
qDebug() << "Spindle stopped.";
// 此处添加数控软件停止主轴等操作的具体实现
}
};
// 3. 命令管理器,用于注册和执行命令
class CommandManager {
public:
using CommandPtr = std::unique_ptr<ICommand>;
// 注册命令,commandName 作为标识
void registerCommand(const QString &commandName, CommandPtr command) {
commands_[commandName] = std::move(command);
}
// 根据命令名称执行对应命令
void executeCommand(const QString &commandName) {
auto it = commands_.find(commandName);
if (it != commands_.end()) {
it->second->execute();
} else {
qWarning() << "Command not found:" << commandName;
}
}
private:
std::unordered_map<QString, CommandPtr> commands_;
};
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
// 主窗口及布局
QWidget window;
window.setWindowTitle("数控软件示例");
QVBoxLayout *layout = new QVBoxLayout(&window);
// 创建两个示例按钮
QPushButton *btnStart = new QPushButton("启动主轴");
QPushButton *btnStop = new QPushButton("停止主轴");
layout->addWidget(btnStart);
layout->addWidget(btnStop);
// 4. 初始化命令管理器并注册具体命令
CommandManager commandManager;
commandManager.registerCommand("start", std::make_unique<SpindleStartCommand>());
commandManager.registerCommand("stop", std::make_unique<SpindleStopCommand>());
// 5. 连接按钮信号到命令执行,通过 lambda 调用 CommandManager 对应方法
QObject::connect(btnStart, &QPushButton::clicked, [&commandManager]() {
commandManager.executeCommand("start");
});
QObject::connect(btnStop, &QPushButton::clicked, [&commandManager]() {
commandManager.executeCommand("stop");
});
window.show();
return app.exec();
}
3. 说明
-
模块化设计:
每个具体命令(例如 SpindleStartCommand、SpindleStopCommand)只负责它自己的功能,后续添加新功能时,只需新建命令类并在 CommandManager 中注册,不用修改原有代码。 -
使用 Qt 信号槽机制:
利用 lambda 表达式直接在连接中调用 CommandManager 的 executeCommand 方法,使得连接逻辑非常直观,不需要中间的 QSignalMapper。 -
扩展性:
如果今后需要新增其他控制功能,只需要实现继承 ICommand 的新命令类,然后在界面中添加对应按钮或其他触发器,通过 CommandManager 注册并连接对应命令即可。 -
C++17特性:
示例中使用了 std::unique_ptr 来管理命令对象以及 lambda 表达式,使得代码更加现代化,同时降低资源管理的复杂度