您的位置:首页 > 游戏 > 游戏 > 黑龙江建设网官网登陆_上海广告传媒公司一览表_网店运营教学_自己建网页

黑龙江建设网官网登陆_上海广告传媒公司一览表_网店运营教学_自己建网页

2025/3/6 2:20:39 来源:https://blog.csdn.net/FallingGod/article/details/145940781  浏览:    关键词:黑龙江建设网官网登陆_上海广告传媒公司一览表_网店运营教学_自己建网页
黑龙江建设网官网登陆_上海广告传媒公司一览表_网店运营教学_自己建网页

概念

命令模式是一种行为型设计模式,它将请求封装成一个对象,从而使你可以用不同的请求对客户进行参数化,对请求排队或者记录请求日志,以及支持可撤销的操作。

定义

命令模式把一个请求或者操作封装到一个对象中,将发出命令的责任和执行命令的责任分割开。这样,命令的发送者不需要知道命令的接收者是谁,也不需要知道命令是如何被执行的,只需要关心如何发出命令。而命令的接收者只需要专注于如何执行命令,实现具体的业务逻辑。

想象一下,此时你手里握着一个智能家居的遥控器,可以控制家里的各种设备(灯、空调、音响)。最初,遥控器的每个按钮直接绑定了设备的操作——比如“开灯”按钮直接硬编码调用了客厅灯.turnOn() 的方法。这种方法看似简单,但是当设备升级的时候(比如换用语音控制的智能灯),你就需要拆开遥控器重新焊接电路;想新增一个“观影模式”(开灯、开空调、关窗帘)时,你又需要继续再遥控器内新增一堆新逻辑。而命令模式(Command) 则是把每一个操作(如开灯、调节温度)封装成独立的 命令对象,例如LightOnCommand包含执行(execute())和撤销(undo())方法,内部持有对灯具的引用。遥控器(调用者)完全无需知道设备细节,只需存储并触发这些命令对象。当你按下“开灯”按钮时,遥控器只是调用lightCommand.execute(),具体是传统灯具还是智能灯执行,它毫不关心。

那么,在这样的设计模式下,就会体现出下述三个优点:

  1. 灵活扩展:新增空调控制,只需创建一个AirconCommand丢给遥控器,无需改动原有代码;
  2. 支持宏命令:将关灯、开空调、降窗帘组合成MovieModeCommand,一键触发复杂操作;
  3. 实现撤销功能:执行命令后,遥控器记录历史,按下“撤销”键即可回退到上一步状态。

这就是命令模式的核心——将“请求”抽象为对象,让调用者和接收者解耦。

组成部分

  • 命令接口(Command):声明了执行命令的抽象方法,所有具体命令类都需要实现这个接口。它定义了一个统一的执行命令的方法,通常命名为execute等,用来规范具体命令类的行为。
  • 具体命令类(ConcreteCommand):实现了命令接口,持有一个接收者对象的引用,在execute方法中调用接收者的相关方法来完成具体的操作。它将命令的执行和接收者的具体行为绑定在一起,实现了命令的具体逻辑。
  • 接收者(Receiver):知道如何执行与请求相关的操作,具体命令对象会调用它的方法来完成命令的执行。它是真正执行命令的对象,负责实现命令所对应的具体业务逻辑。
  • 调用者(Invoker):负责调用命令对象的execute方法来发起命令。它不直接与接收者交互,而是通过命令对象来间接执行操作,它可以设置命令对象,并在需要的时候触发命令的执行。
  • 客户端(Client):创建具体命令对象,并设置命令对象的接收者。在客户端中,将命令的发送者和接收者进行解耦,使得发送者不需要了解接收者的具体实现,只需要关心命令的发送。

在这里插入图片描述

工作原理

  1. 定义接口:先定义一个命令接口,声明execute等方法规范命令行为。
  2. 创建具体命令类:创建实现命令接口的具体命令类,内部持有接收者对象,在execute方法中调用接收者方法完成具体操作。
  3. 设置接收者:创建接收者对象,它负责执行具体业务逻辑,具体命令类将其作为成员变量持有。
  4. 设置调用者:创建调用者,它持有命令对象,通过调用命令对象的execute方法来发起命令,并不直接与接收者交互。
  5. 客户端操作:客户端负责创建具体命令对象,设置好接收者,并将命令对象传递给调用者。
  6. 命令执行:调用者执行命令时,命令对象调用接收者的相应方法完成操作,实现请求发送者和接收者的解耦。若支持撤销,命令对象会记录执行前状态,在撤销时调用接收者方法恢复状态。

示例

我么使用智能家居遥控器的例子来编写示例代码。

类图

在这里插入图片描述

C++实现

#include <iostream>
#include <vector>// 前向声明
class Receiver;// 命令接口
class Command {
public:virtual void execute() = 0;virtual void undo() = 0;virtual ~Command() = default;
};// 接收者:灯具
class Light {
public:void turnOn() {std::cout << "Light is on." << std::endl;}void turnOff() {std::cout << "Light is off." << std::endl;}
};// 接收者:空调
class AirConditioner {
public:void turnOn() {std::cout << "Air conditioner is on." << std::endl;}void turnOff() {std::cout << "Air conditioner is off." << std::endl;}
};// 开灯命令
class LightOnCommand : public Command {
private:Light* light;
public:explicit LightOnCommand(Light* light) : light(light) {}void execute() override {light->turnOn();}void undo() override {light->turnOff();}
};// 关灯命令
class LightOffCommand : public Command {
private:Light* light;
public:explicit LightOffCommand(Light* light) : light(light) {}void execute() override {light->turnOff();}void undo() override {light->turnOn();}
};// 开空调命令
class AirConditionerOnCommand : public Command {
private:AirConditioner* ac;
public:explicit AirConditionerOnCommand(AirConditioner* ac) : ac(ac) {}void execute() override {ac->turnOn();}void undo() override {ac->turnOff();}
};// 关空调命令
class AirConditionerOffCommand : public Command {
private:AirConditioner* ac;
public:explicit AirConditionerOffCommand(AirConditioner* ac) : ac(ac) {}void execute() override {ac->turnOff();}void undo() override {ac->turnOn();}
};// 调用者:遥控器
class RemoteControl {
private:std::vector<Command*> commands;std::vector<Command*> undoCommands;
public:void setCommand(Command* command) {commands.push_back(command);}void pressButton(int index) {if (index < commands.size()) {commands[index]->execute();undoCommands.push_back(commands[index]);}}void pressUndoButton() {if (!undoCommands.empty()) {Command* lastCommand = undoCommands.back();lastCommand->undo();undoCommands.pop_back();}}~RemoteControl() {for (auto command : commands) {delete command;}}
};// 客户端代码
int main() {// 创建接收者Light light;AirConditioner ac;// 创建命令Command* lightOn = new LightOnCommand(&light);Command* lightOff = new LightOffCommand(&light);Command* acOn = new AirConditionerOnCommand(&ac);Command* acOff = new AirConditionerOffCommand(&ac);// 创建调用者RemoteControl remote;// 设置命令remote.setCommand(lightOn);remote.setCommand(lightOff);remote.setCommand(acOn);remote.setCommand(acOff);// 执行命令remote.pressButton(0); // 开灯remote.pressButton(2); // 开空调remote.pressUndoButton(); // 撤销开空调remote.pressUndoButton(); // 撤销开灯return 0;
}

Java实现

// 命令接口
interface Command {void execute();void undo();
}// 接收者:灯具
class Light {public void turnOn() {System.out.println("Light is on.");}public void turnOff() {System.out.println("Light is off.");}
}// 接收者:空调
class AirConditioner {public void turnOn() {System.out.println("Air conditioner is on.");}public void turnOff() {System.out.println("Air conditioner is off.");}
}// 开灯命令
class LightOnCommand implements Command {private Light light;public LightOnCommand(Light light) {this.light = light;}@Overridepublic void execute() {light.turnOn();}@Overridepublic void undo() {light.turnOff();}
}// 关灯命令
class LightOffCommand implements Command {private Light light;public LightOffCommand(Light light) {this.light = light;}@Overridepublic void execute() {light.turnOff();}@Overridepublic void undo() {light.turnOn();}
}// 开空调命令
class AirConditionerOnCommand implements Command {private AirConditioner ac;public AirConditionerOnCommand(AirConditioner ac) {this.ac = ac;}@Overridepublic void execute() {ac.turnOn();}@Overridepublic void undo() {ac.turnOff();}
}// 关空调命令
class AirConditionerOffCommand implements Command {private AirConditioner ac;public AirConditionerOffCommand(AirConditioner ac) {this.ac = ac;}@Overridepublic void execute() {ac.turnOff();}@Overridepublic void undo() {ac.turnOn();}
}// 调用者:遥控器
class RemoteControl {private java.util.ArrayList<Command> commands = new java.util.ArrayList<>();private java.util.ArrayList<Command> undoCommands = new java.util.ArrayList<>();public void setCommand(Command command) {commands.add(command);}public void pressButton(int index) {if (index < commands.size()) {Command command = commands.get(index);command.execute();undoCommands.add(command);}}public void pressUndoButton() {if (!undoCommands.isEmpty()) {Command lastCommand = undoCommands.remove(undoCommands.size() - 1);lastCommand.undo();}}
}// 客户端代码
public class Main {public static void main(String[] args) {// 创建接收者Light light = new Light();AirConditioner ac = new AirConditioner();// 创建命令Command lightOn = new LightOnCommand(light);Command lightOff = new LightOffCommand(light);Command acOn = new AirConditionerOnCommand(ac);Command acOff = new AirConditionerOffCommand(ac);// 创建调用者RemoteControl remote = new RemoteControl();// 设置命令remote.setCommand(lightOn);remote.setCommand(lightOff);remote.setCommand(acOn);remote.setCommand(acOff);// 执行命令remote.pressButton(0); // 开灯remote.pressButton(2); // 开空调remote.pressUndoButton(); // 撤销开空调remote.pressUndoButton(); // 撤销开灯}
}

代码解释

  1. Command 接口:定义了命令的基本操作,包括execute()用于执行命令和undo()用于撤销命令。
  2. 接收者类(Light 和 AirConditioner):这些类表示具体的设备,包含设备的基本操作方法,如turnOn()turnOff()
  3. 具体命令类(LightOnCommand、LightOffCommand、AirConditionerOnCommand、AirConditionerOffCommand):实现了Command接口,内部持有对接收者的引用,在execute()方法中调用接收者的相应操作,undo()方法则执行相反的操作。
  4. 调用者类(RemoteControl):负责存储和执行命令,通过setCommand()方法设置命令,pressButton()方法执行指定索引的命令,pressUndoButton()方法撤销上一个执行的命令。
  5. 客户端代码(main 函数):创建接收者、命令和调用者对象,设置命令并执行相应操作。

通过这种方式,遥控器(调用者)不需要知道具体设备的细节,只需要操作命令对象,提高了系统的可扩展性和可维护性。当设备升级或新增功能时,只需要创建新的命令类,而不需要修改遥控器的代码。

设计原则

命令模式主要遵循以下几个重要的设计原则:

单一职责原则(Single Responsibility Principle)

  • 解释:该原则强调一个类应该仅有一个引起它变化的原因。在命令模式中,每个类都有其明确的职责。
  • 具体体现
    • 命令接口和具体命令类Command接口只负责定义命令的执行和撤销等操作规范,而具体的命令类(如LightOnCommandLightOffCommand)只负责实现特定的命令逻辑,比如在execute方法中调用接收者的特定操作,它们不涉及其他无关的功能。
    • 接收者类:像LightAirConditioner类,只专注于自身设备的具体操作,如开启、关闭等,不参与命令的管理和调用逻辑。
    • 调用者类RemoteControl类只负责存储和执行命令,不关心命令具体如何实现以及接收者的具体操作细节。

开闭原则(Open/Closed Principle)

  • 解释:软件实体(类、模块、函数等)应该对扩展开放,对修改关闭。即当需求发生变化时,应该通过扩展软件实体的行为来实现,而不是修改已有的代码。
  • 具体体现
    • 新增命令:当需要添加新的命令时,比如增加一个调节灯光亮度的命令,只需要创建一个新的具体命令类(如LightBrightnessCommand),实现Command接口,而不需要修改现有的Command接口、调用者类(RemoteControl)和接收者类(Light)。
    • 新增接收者:如果要引入新的设备,如智能窗帘,只需要创建新的接收者类(Curtain)和对应的命令类(如CurtainOpenCommandCurtainCloseCommand),原有的系统结构和代码基本无需改动。

依赖倒置原则(Dependency Inversion Principle)

  • 解释:高层模块不应该依赖低层模块,二者都应该依赖抽象;抽象不应该依赖细节,细节应该依赖抽象。
  • 具体体现
    • 调用者与命令的关系RemoteControl作为高层模块,不直接依赖具体的命令类(如LightOnCommand),而是依赖抽象的Command接口。这样,当具体命令类发生变化时,RemoteControl类不受影响。
    • 具体命令类与接收者的关系:具体命令类依赖于接收者的抽象(虽然这里接收者没有显式的抽象,但可以理解为依赖接收者的操作接口),而不是具体的接收者实现细节。例如,LightOnCommand只关心Light类有turnOn方法,而不关心Light类内部具体如何实现开启灯光的操作。

迪米特法则(Law of Demeter)

  • 解释:一个对象应该对其他对象有最少的了解,即一个类应该尽量减少与其他类之间的交互,只与直接的朋友通信。
  • 具体体现
    • 调用者与接收者的隔离RemoteControl作为调用者,它只与Command对象交互,不需要了解接收者(如LightAirConditioner)的具体实现。通过命令对象作为中间层,降低了调用者与接收者之间的耦合度。
    • 具体命令类的独立性:具体命令类(如LightOnCommand)只与它的直接朋友(Light接收者和Command接口)进行交互,不与其他无关的类产生不必要的联系。

命令模式的优缺点

优点

1. 解耦请求发送者和接收者
  • 发送者(如调用者类)不需要知道接收者(如具体执行操作的对象)是谁,也不需要了解接收者的具体实现细节。发送者只需要通过命令对象来间接执行操作,降低了系统的耦合度。例如在智能家居系统中,遥控器(发送者)不需要知道灯具(接收者)的具体电路和控制方式,只需要触发相应的命令对象即可。
2. 可扩展性强
  • 方便添加新的命令,只需要创建新的具体命令类并实现命令接口即可,不会影响到现有的其他类和模块。当系统需要新增功能时,如在智能家居系统中增加调节窗帘开合的功能,只需创建新的命令类和对应的接收者类,而无需修改遥控器(调用者)的代码。
3. 支持命令的撤销和重做
  • 可以在命令对象中记录命令执行前的状态,在需要撤销时恢复到之前的状态,重做时再次执行命令。这在许多应用场景中非常有用,如文本编辑器中的撤销和重做操作,通过命令模式可以很方便地实现。
4. 便于实现命令队列和日志记录
  • 可以将命令对象放入队列中,按照顺序依次执行,实现命令的排队执行。同时,也可以方便地记录命令的执行日志,便于系统的监控和故障排查。例如在多任务处理系统中,可以将一系列命令放入队列中,按顺序依次处理。
5. 符合开闭原则
  • 对扩展开放,对修改关闭。当需要增加新的命令时,只需要扩展新的具体命令类,而不需要修改现有的调用者和其他命令类,提高了系统的可维护性。

缺点

1. 类的数量增加
  • 每个具体的命令都需要创建一个对应的具体命令类,当系统中的命令较多时,会导致类的数量急剧增加,增加了系统的复杂性和维护成本。例如在一个复杂的游戏系统中,各种不同的操作都需要封装成命令类,可能会产生大量的类文件。
2. 命令过多时管理复杂
  • 如果系统中有大量的命令,管理这些命令对象会变得困难。需要有良好的组织和管理机制,否则会导致代码混乱,降低代码的可读性和可维护性。
3. 增加系统的理解难度
  • 由于引入了命令对象、调用者、接收者等多个角色和层次,对于不熟悉命令模式的开发者来说,理解和掌握系统的整体架构和工作流程会有一定的难度。
4. 性能开销
  • 每个命令都封装成一个对象,会带来一定的内存开销和对象创建、销毁的性能开销。特别是在对性能要求较高的系统中,这种开销可能会成为一个问题。

注意事项

设计层面

  1. 合理设计命令接口
    • 命令接口应简洁且具有通用性,只包含必要的方法,如execute()undo()。过多的方法会增加具体命令类的实现复杂度,破坏单一职责原则。
    • 接口的命名要清晰,能准确表达命令的基本操作,方便其他开发者理解和使用。
  2. 控制类的数量
    • 命令模式可能会导致类的数量增多,尤其是在系统功能复杂、命令多样的情况下。要合理规划命令类的层次结构,避免类的泛滥。可以通过抽象命令类或命令组来减少重复代码和类的数量。
    • 例如,对于具有相似操作的命令,可以抽象出一个基类,让具体命令类继承该基类,复用公共的代码逻辑。
  3. 遵循设计原则
    • 严格遵循单一职责原则,确保每个类和接口只负责单一的功能。例如,具体命令类只负责封装特定的操作,接收者类只负责执行具体的业务逻辑。
    • 遵循开闭原则,在需要扩展新命令时,通过创建新的具体命令类来实现,而不是修改现有的代码。这样可以提高系统的可维护性和扩展性。

实现层面

  1. 内存管理
    • 由于命令模式会创建大量的命令对象,要注意内存的使用情况。特别是在命令频繁执行和撤销的场景下,可能会产生大量的临时对象,导致内存占用过高。
    • 可以考虑使用对象池技术来复用命令对象,减少对象的创建和销毁开销,提高系统的性能。
  2. 命令撤销和重做的实现
    • 如果需要支持命令的撤销和重做功能,要确保在命令对象中正确记录执行前的状态。在实现undo()方法时,要保证能准确地恢复到执行命令之前的状态。
    • 同时,要处理好撤销和重做操作的顺序和边界条件,避免出现状态不一致的问题。
  3. 线程安全
    • 在多线程环境下使用命令模式时,要考虑线程安全问题。如果多个线程同时操作命令队列或命令对象,可能会导致数据不一致或其他并发问题。
    • 可以通过同步机制(如synchronized关键字或Lock接口)来保证线程安全,或者使用线程安全的数据结构来存储命令。

使用场景层面

  1. 适用场景判断
    • 命令模式适用于需要将请求的发送者和接收者解耦、支持命令的撤销和重做、实现命令队列和日志记录等场景。在使用前要仔细分析系统需求,判断是否真正适合使用命令模式。
    • 如果系统的功能简单,请求和执行逻辑紧密耦合,使用命令模式可能会增加系统的复杂度,得不偿失。
  2. 与其他模式结合使用
    • 在实际开发中,命令模式可以与其他设计模式结合使用,以更好地满足系统需求。例如,与观察者模式结合,当命令执行完成后通知相关的观察者;与组合模式结合,实现命令的组合和嵌套执行。
    • 但在结合使用时,要注意不同模式之间的协调和兼容性,避免引入新的问题。

应用场景

1. 图形用户界面(GUI)应用程序

  • 按钮和菜单操作:在各种桌面应用程序、Web 应用程序或移动应用程序的图形用户界面中,用户通过点击按钮、选择菜单项等操作来触发各种功能。每个操作都可以封装成一个命令对象。例如,在文本编辑器中,“保存”“复制”“粘贴” 等操作对应的按钮点击事件可以分别封装成SaveCommandCopyCommandPasteCommand等命令对象。这样,GUI 组件(如按钮)作为调用者,只需要调用命令对象的execute()方法,而不需要关心具体的操作是如何实现的。
  • 撤销和重做功能:命令模式天然支持撤销和重做操作。在 GUI 应用中,这是一个非常常见且重要的功能。比如在图像编辑软件中,用户进行的每一步操作(如裁剪、调色、添加滤镜等)都可以封装成命令对象。当用户点击 “撤销” 按钮时,系统可以依次调用已执行命令对象的undo()方法,将图像恢复到之前的状态;点击 “重做” 按钮时,则可以再次调用这些命令对象的execute()方法。

2. 游戏开发

  • 游戏操作控制:游戏中的角色移动、攻击、释放技能等操作都可以使用命令模式来实现。例如,在一个角色扮演游戏中,玩家按下键盘上的某个按键来控制角色向前移动,这个操作可以封装成MoveForwardCommand命令对象。游戏控制器作为调用者,根据玩家的输入调用相应命令对象的execute()方法。这样可以方便地实现游戏操作的自定义和扩展,例如可以通过修改命令对象来改变角色的移动速度或攻击方式。
  • 游戏回放和存档:命令模式可以记录游戏中的所有操作,这些操作记录可以用于游戏回放。同时,也可以将这些命令序列保存到文件中,实现游戏存档功能。当玩家加载存档时,系统可以依次执行这些命令,将游戏状态恢复到存档时的状态。

3. 工作流系统

  • 任务调度和执行:在工作流系统中,每个任务的执行可以看作是一个命令。例如,在一个项目管理系统中,任务的分配、审批、完成等操作都可以封装成不同的命令对象。工作流引擎作为调用者,根据工作流的规则依次调用这些命令对象的execute()方法,完成任务的调度和执行。
  • 流程撤销和恢复:如果工作流中某个步骤出现错误或需要修改,命令模式的撤销和重做功能可以方便地实现流程的回退和恢复。例如,在一个审批流程中,如果审批人员误操作通过了一个不应该通过的申请,可以通过撤销相应的审批命令,将流程恢复到之前的状态。

4. 多线程和分布式系统

  • 任务队列和异步处理:在多线程或分布式系统中,命令模式可以用于实现任务队列和异步处理。将需要执行的任务封装成命令对象,放入任务队列中,多个工作线程或分布式节点从队列中取出命令对象并执行。这样可以实现任务的解耦和异步执行,提高系统的并发性能和可扩展性。
  • 日志记录和故障恢复:在分布式系统中,命令模式可以方便地记录每个操作的日志。当系统出现故障时,可以根据日志中的命令序列进行故障恢复。例如,在一个分布式数据库系统中,对数据库的增删改操作可以封装成命令对象,记录操作日志。当数据库节点出现故障重启时,可以根据日志中的命令对象重新执行操作,保证数据的一致性。

5. 智能家居系统

  • 设备控制:智能家居系统中,用户可以通过手机 APP 或智能遥控器来控制各种智能设备,如灯光、空调、窗帘等。每个设备的操作(如开灯、调节温度、打开窗帘等)都可以封装成命令对象。智能控制中心作为调用者,根据用户的指令调用相应命令对象的execute()方法,实现对设备的远程控制。
  • 场景模式设置:智能家居系统通常支持场景模式,如 “回家模式”“睡眠模式” 等。每个场景模式可以看作是一组命令的集合。例如,“回家模式” 可以包含打开灯光、打开空调、拉开窗帘等命令。当用户选择某个场景模式时,系统依次执行该场景模式下的所有命令对象。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com