【声明】本题目来源于卡码网(题目页面 (kamacoder.com))
【提示:如果不想看文字介绍,可以直接跳转到C++编码部分】
-- 什么是命令模式 (第15种模式)
命令模式是⼀种⾏为型设计模式,其允许将请求封装成⼀个对象(命令对象,包含执⾏操作所需的所有信息),并将命令对象按照⼀定的顺序存储在队列中,然后再逐⼀调用执⾏,这些命令也可以⽀持反向操作,进⾏撤销和重做。
这样⼀来,发送者只需要触发命令就可以完成操作,不需要知道接受者的具体操作,从⽽实现两者间的解耦。
举个现实中的应⽤场景,遥控器可以控制不同的设备,在命令模式中,可以假定每个按钮都是⼀个命令对象,包含执⾏特定操作的命令,不同设备对同⼀命令的具体操作也不同,这样就可以⽅便的添加设备和命令对象。
命令模式包含以下⼏个基本⻆⾊:
以Java代码先作以说明:
1. 定义执⾏操作的接⼝:包含⼀个execute ⽅法。有的时候还会包括unExecute ⽅法,表示撤销命令。
public interface Command {
void execute();
}
2. 定义接受者类,知道如何实施与执⾏⼀个请求相关的操作。
public class Receiver {
public void action() {
// 执⾏操作
}
}
3. 实现命令接⼝,执⾏具体的操作。
public class ConcreteCommand implements Command {
// 接收者对象
private Receiver receiver;
public ConcreteCommand(Receiver receiver) {
this.receiver = receiver;
}
@Override
public void execute() {
// 调⽤接收者相应的操作
receiver.action();
}
}
4.
(1)定义调用者类,调⽤命令对象执⾏请求。
public class Invoker {
private Command command;
public Invoker(Command command) {
this.command = command;
}
public void executeCommand() {
command.execute();
}
}
(2)调⽤者类中可以维护⼀个命令队列或者“撤销栈”,以⽀持批处理和撤销命令。
/**
* @version Copyright (c) 2024 NCDC, Servo。 Unpublished - All rights reserved
* @file CommandMode
* @brief 命令模式
* @autor 写代码的小恐龙er
* @date 2024/01/17
*/
import java.util.LinkedList;
import java.util.Queue;
import java.util.Stack;
// 调⽤者类:命令队列和撤销请求
class Invoker {
private Queue commandQueue; // 命令队列
private Stack undoStack; // 撤销栈
public Invoker() {
this.commandQueue = new LinkedList<>();
this.undoStack = new Stack<>();
}
// 设置命令并执⾏
public void setAndExecuteCommand(Command command) {
command.execute();
commandQueue.offer(command);
undoStack.push(command);
}
// 撤销上⼀个命令
public void undoLastCommand() {
if (!undoStack.isEmpty()) {
Command lastCommand = undoStack.pop();
lastCommand.undo(); // 需要命令类实现 undo ⽅法
commandQueue.remove(lastCommand);
} else {
System.out.println("No command to undo.");
}
}
// 执⾏命令队列中的所有命令
public void executeCommandsInQueue() {
for (Command command : commandQueue) {
command.execute();
}
}
}
5.客户端使用,创建具体的命令对象和接收者对象,然后进⾏组装。
public class Main {
public static void main(String[] args) {
Receiver receiver = new Receiver();
Command command = new ConcreteCommand(receiver);
Invoker invoker = new Invoker(command);
invoker.executeCommand();
}
}
命令模式在需要将请求封装成对象、⽀持撤销和重做、设计命令队列等情况下,都是⼀个有效的设计模式。
但是对于每个命令,都会有⼀个具体命令类,这可能导致类的数量急剧增加,增加了系统的复杂性。
命令模式同样有着很多现实场景的应用,⽐如Git中的很多操作,如提交(commit)、合并(merge)等,都可以看作是命令模式的应⽤,用户通过执⾏相应的命令来操作版本库。Java的GUI编程中,很多事件处理机制也都使⽤了命令模式。例如,每个按钮都有⼀个关联的 Action ,它代表⼀个命令,按钮的点击触发 Action 的执⾏。
小明去奶茶店买奶茶,他可以通过在自助点餐机上来点不同的饮品,请你使用命令模式设计一个程序,模拟这个自助点餐系统的功能。
输出执行完所有点单后的制作情况,每行输出一种饮品的制作情况。如果制作完成,输出 "XXX is ready!",其中 XXX 表示饮品名称。
/**
* @version Copyright (c) 2024 NCDC, Servo。 Unpublished - All rights reserved
* @file CommandMode.hpp
* @brief 命令模式
* @autor 写代码的小恐龙er
* @date 2024/01/17
*/
#include
#include
#include
using namespace std;
// 前置声明
// 命令接口类
class Command;
// 具体命令类 -- 点餐命令
class OrderCommand;
// 接收者类
class DrinkMaker;
// 调用者类
class OrderMachine;
// 实现
// 1. 命令接口类
class Command
{
public:
// 接口函数
virtual void Execute() = 0;
};
// 2. 接收者类
class DrinkMaker {
// 成员函数
public:
void makeDrink(string drinkName) {
std::cout << drinkName << " is ready!" << endl;
}
};
// 3. 具体命令类 -- 点餐命令
class OrderCommand : public Command
{
// 成员数据
private:
string _drinkNmae;
DrinkMaker *_receiver;
// 成员函数
public:
// 构造函数
OrderCommand(string drinkNmae, DrinkMaker *receiver){
this->_drinkNmae = drinkNmae;
this->_receiver = receiver;
}
// 重载命令执行函数
void Execute() override{
_receiver->makeDrink(_drinkNmae);
}
};
// 4. 调用者类
class OrderMachine
{
// 成员数据
private:
// 调用的命令基类
Command *_command;
// 成员函数
public:
OrderMachine(Command *command){
this->_command = command;
}
// 调用命令的执行
void ExecuteOrder(){
if(_command != nullptr){
_command->Execute();
}
return;
}
};
// 客户端
int main()
{
// 点单数量
int orderNum = 0;
std::cin >> orderNum;
// 创建命令基类
Command *command = nullptr;
// 创建调用者类 后续进行堆区赋值
OrderMachine *orderMachine = nullptr;
// 创建接收者类
DrinkMaker *drinkMaker = new DrinkMaker();
// 遍历
for(int i = 0; i < orderNum; i++){
// 饮品名称
string name;
std::cin >> name;
// 构造类
command = new OrderCommand(name, drinkMaker);
orderMachine = new OrderMachine(command);
// 执行命令
orderMachine->ExecuteOrder();
}
// 析构
delete drinkMaker;
drinkMaker = nullptr;
if(command != nullptr){
delete command;
command = nullptr;
}
if(orderMachine != nullptr){
delete orderMachine;
orderMachine = nullptr;
}
return 0;
}
......
To be continued.