C++设计模式中的命令模式是一种行为设计模式,它封装了一个请求作为一个对象,从而让你使用不同的请求把客户端与服务操作解耦。这种模式有着广泛的用途和多种变体。以下是对命令模式的详细解析:
动机与意图
- 解耦调用者和接收者:命令模式的主要动机是将请求的发送者与接收者解耦,使得调用者不需要知道接收者的具体实现细节。
- 支持可撤销操作:命令模式可以记录每个命令的执行状态,从而实现撤销和重做功能,这在一些需要支持撤销操作的场景中非常有用。
- 支持命令的排队和日志记录:命令对象可以被存储在一个队列中,从而实现命令的排队执行。同时,也可以记录每个命令的执行日志,方便后续的问题排查和分析。
- 增加系统的灵活性:命令模式允许在运行时动态地添加新的命令,而不需要修改已有的代码,使系统更加灵活,可以方便地扩展新的功能。
适用场合
- GUI操作:在图形用户界面程序中,常常需要处理用户点击事件,使用命令模式可以方便地处理这些事件,并支持撤销和重做功能。
- 多线程环境:在多线程环境中,命令模式可以用来管理线程任务的执行序列。
- 需要记录操作日志的场景:通过命令对象,可以记录每个操作的详细信息,方便后续审计和分析。
代码示例
1. GUI操作
假设我们有一个图形用户界面,用户可以通过点击按钮来执行某些操作,我们使用命令模式来处理这些按钮点击事件,并支持撤销和重做功能。
cpp
#include <iostream>
#include <vector>
// 接收者类
class Receiver {
public:
void action() {
std::cout << "执行具体操作" << std::endl;
}
};
// 命令接口
class Command {
public:
virtual void execute() = 0;
virtual void undo() = 0;
};
// 具体命令类
class ConcreteCommand : public Command {
private:
Receiver* receiver;
public:
ConcreteCommand(Receiver* receiver) : receiver(receiver) {}
void execute() override {
receiver->action();
}
void undo() override {
std::cout << "撤销具体操作" << std::endl;
}
};
// 调用者类
class Invoker {
private:
Command* command;
std::vector<Command*> commandHistory;
public:
void setCommand(Command* cmd) {
command = cmd;
}
void executeCommand() {
command->execute();
commandHistory.push_back(command);
}
void undoLastCommand() {
if (!commandHistory.empty()) {
commandHistory.back()->undo();
commandHistory.pop_back();
}
}
};
// 按钮类
class Button {
private:
Invoker* invoker;
Command* command;
public:
Button(Invoker* invoker, Command* command) : invoker(invoker), command(command) {}
void click() {
invoker->setCommand(command);
invoker->executeCommand();
}
};
int main() {
Receiver* receiver = new Receiver();
ConcreteCommand* command = new ConcreteCommand(receiver);
Invoker* invoker = new Invoker();
Button* button = new Button(invoker, command);
// 用户点击按钮
button->click();
// 撤销上一次操作
invoker->undoLastCommand();
delete button;
delete invoker;
delete command;
delete receiver;
return 0;
}
2. 多线程环境
假设我们有一个多线程环境,需要管理任务的执行序列。我们可以使用命令模式来管理这些任务。
cpp
#include <iostream>
#include <vector>
#include <thread>
#include <mutex>
#include <condition_variable>
// 接收者类
class Receiver {
public:
void action() {
std::cout << "执行具体操作" << std::endl;
}
};
// 命令接口
class Command {
public:
virtual void execute() = 0;
};
// 具体命令类
class ConcreteCommand : public Command {
private:
Receiver* receiver;
public:
ConcreteCommand(Receiver* receiver) : receiver(receiver) {}
void execute() override {
receiver->action();
}
};
// 任务队列类
class TaskQueue {
private:
std::vector<Command*> queue;
std::mutex mutex;
std::condition_variable cv;
bool done;
public:
TaskQueue() : done(false) {}
void addTask(Command* task) {
std::lock_guard<std::mutex> lock(mutex);
queue.push_back(task);
cv.notify_one();
}
void processTasks() {
while (true) {
Command* task;
{
std::unique_lock<std::mutex> lock(mutex);
cv.wait(lock, [this] { return !queue.empty() || done; });
if (done && queue.empty()) {
return;
}
task = queue.front();
queue.erase(queue.begin());
}
task->execute();
}
}
void stop() {
{
std::lock_guard<std::mutex> lock(mutex);
done = true;
}
cv.notify_one();
}
};
int main() {
Receiver* receiver = new Receiver();
ConcreteCommand* command1 = new ConcreteCommand(receiver);
ConcreteCommand* command2 = new ConcreteCommand(receiver);
TaskQueue taskQueue;
std::thread worker(&TaskQueue::processTasks, &taskQueue);
taskQueue.addTask(command1);
taskQueue.addTask(command2);
taskQueue.stop();
worker.join();
delete command2;
delete command1;
delete receiver;
return 0;
}
3. 需要记录操作日志的场景
假设我们需要记录每个操作的详细信息,可以使用命令模式来实现这一点。
cpp
#include <iostream>
#include <vector>
#include <fstream>
// 接收者类
class Receiver {
public:
void action() {
std::cout << "执行具体操作" << std::endl;
}
};
// 命令接口
class Command {
public:
virtual void execute() = 0;
virtual void log() = 0;
};
// 具体命令类
class ConcreteCommand : public Command {
private:
Receiver* receiver;
std::string logMessage;
public:
ConcreteCommand(Receiver* receiver, std::string logMessage) : receiver(receiver), logMessage(logMessage) {}
void execute() override {
receiver->action();
}
void log() override {
std::ofstream logFile("log.txt", std::ios_base::app);
logFile << logMessage << std::endl;
logFile.close();
}
};
// 调用者类
class Invoker {
private:
Command* command;
std::vector<Command*> commandHistory;
public:
void setCommand(Command* cmd) {
command = cmd;
}
void executeCommand() {
command->execute();
command->log();
commandHistory.push_back(command);
}
};
int main() {
Receiver* receiver = new Receiver();
ConcreteCommand* command1 = new ConcreteCommand(receiver, "命令1执行");
ConcreteCommand* command2 = new ConcreteCommand(receiver, "命令2执行");
Invoker* invoker = new Invoker();
invoker->setCommand(command1);
invoker->executeCommand();
invoker->setCommand(command2);
invoker->executeCommand();
delete invoker;
delete command2;
delete command1;
delete receiver;
return 0;
}
基于该模式特点的软件架构模式
基于命令模式的特点,可以构建一种灵活且可扩展的软件架构。在这种架构中,各个组件之间的耦合度被降低,使得系统更加容易维护和扩展。同时,由于命令对象可以被存储和传递,因此可以方便地实现操作的撤销、重做、排队和日志记录等功能。这种架构特别适用于需要灵活处理用户请求、支持撤销和重做操作、以及需要记录操作日志的系统。
总的来说,命令模式是一种非常实用的设计模式,它通过将请求封装为对象来解耦调用者和接收者,增加了系统的灵活性和可扩展性。同时,根据不同的需求和应用场景,可以灵活地运用命令模式的各种变体来满足实际需求。