我们都熟悉电视遥控器,它有许多按钮,每个按钮都有确定的功能。你按下电源键电视就会打开,再按下一次电视就会关闭。编程世界里也有这种模式,这就是我们说的命令模式。
命令模式是一种设计模式,它把一个请求或操作封装到一个对象中。这意味着命令模式允许我们将操作分开,我们可以控制何时和如何执行它们。
遥控你的代码:命令模式的编程实战
了解命令模式的最好方式,就是通过代码来看看它如何运作。
假设我们要制作一个游戏模拟器,运行器可以执行两个命令,启动和关闭。
// 游戏模拟器操作接口 interface Command { void execute(); } class GameConsole { public void start_game() { System.out.println("游戏开启!"); } public void stop_game() { System.out.println("游戏关闭!"); } } // 启动命令 class StartCommand implements Command { GameConsole console; public StartCommand(GameConsole console) { this.console = console; } public void execute() { console.start_game(); } } // 停止命令 class StopCommand implements Command { GameConsole console; public StopCommand(GameConsole console) { this.console = console; } public void execute() { console.stop_game(); } } // 游戏模拟器 class GameSimulator { HashMap<String, Command> commands = new HashMap<>(); public void register(String cmd_name, Command command) { commands.put(cmd_name, command); } public void execute(String cmd_name) { if (commands.containsKey(cmd_name)) { Command command = commands.get(cmd_name); command.execute(); } else { System.out.println("Unknown command!"); } } } public class Main { public static void main(String[] args) { GameSimulator simulator = new GameSimulator(); GameConsole console = new GameConsole(); simulator.register("START", new StartCommand(console)); simulator.register("STOP", new StopCommand(console)); simulator.execute("START"); simulator.execute("STOP"); } }
在上面的Java代码中,我们创建了一个游戏模拟器,它可以执行两种命令:启动和停止。我们使用了接口来定义Command,它只有一个execute方法。然后,我们创建了两个Command的实现类,即启动命令(StartCommand)和停止命令(StopCommand)。
命令模式如何实现撤销/恢复?
撤销和恢复的操作是通过以下两步实现的:
- 每次执行命令时,将该命令对象存储在历史记录列表中(如stack) 。
- 撤销操作其实就是从历史记录中取出最近的命令,并执行它的反操作。如果该命令对象中包含了执行前对象的状态信息,则撤销操作可以通过恢复这些状态来实现。
让我们来看一个Java代码例子,这个例子展示了如何使用命令模式实现撤销功能:
interface Command { void execute(); void undo(); } class AddTextCommand implements Command { private String textToAdd; private TextEditor editor; public AddTextCommand(String textToAdd, TextEditor editor) { this.textToAdd = textToAdd; this.editor = editor; } public void execute() { editor.addText(textToAdd); } public void undo() { editor.removeText(textToAdd); } } class TextEditor { private StringBuilder text = new StringBuilder(); public void addText(String textToAdd) { text.append(textToAdd); } public void removeText(String textToRemove) { int index = text.lastIndexOf(textToRemove); if (index != -1) { text.delete(index, index + textToRemove.length()); } } public void printText() { System.out.println(text.toString()); } } class TextEditorDriver { private Stack<Command> commandHistory = new Stack<>(); public void executeCommand(Command cmd) { cmd.execute(); commandHistory.push(cmd); } public void undoLastCommand() { if (!commandHistory.isEmpty()) { Command lastCmd = commandHistory.pop(); lastCmd.undo(); } } }
在这段代码中,我们首先定义了一个Command接口,并添加了一个新的方法undo。我们的每个具体命令都必须定义这两个方法。
在TextEditorDriver类中,我们维护了一个命令历史stack。每次执行命令时,我们都将命令添加到这个stack中。当我们要撤销操作时,我们只需取出最新的命令,并调用它的undo方法。
在命令模式中,撤销和恢复功能的实现核心是在每个具体的命令中,都保存了足够的信息以便在需要时可以反转其效果。通过使用存储的命令历史,我们不仅可以撤销命令,还可以重做它们。
命令模式的深入理解
以上的小例子虽然简单,但是已经显示了命令模式的三大优点:
- 解耦调用者与接收者:命令模式可以解耐调用者和接收者之间的耦合,将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。
- 命令可扩展:你可以增加新的命令而完全不改变现有的代码。
- 支持撤销、队列、宏命令等:命令模式的另一个优点就是可以后期扩展新的特性。比如可以添加命令历史来实现撤销和恢复等功能,或者搭建任务队列进行后台处理等。
命令模式是创建型和行为型设计模式的交织,使用它,你就可以变得像使用遥控器一样自由自在地控制你的代码。
命令模式是创建型和行为型设计模式的交织怎么理解?
我们之前提到命令模式是创建型和行为型设计模式的交织。那么这句话是怎么理解的呢?
设计模式通常被分为三种类型:创建型,结构型和行为型。
- 创建型模式:这类模式主要处理对象创建机制,尝试在创建对象的过程中增加更多的灵活性和效率。简单工厂、抽象工厂、建造者、原型、单例等都是创建型模式。
- 结构型模式:这类模式主要关注对象组合,或者换句话说,实体之间如何互相使用。这些模式能保证系统中各部分之间相互关系的清晰地定义。适配器、桥接、组合、装饰、外观、享元、代理等都是结构型模式。
- 行为型模式:这类模式专注于对象间的通信,它们的主要目的是增强对象间的通信以及如何控制复杂系统中多个对象的协作。策略、模板方法、观察者、迭代器、责任链、命令、备忘录、状态、访问者等都是行为型模式。
命令模式是一个行为型模式,因为它解决的主要问题是将行为请求者和接收者进行解耦,实现请求的封装。这让请求者不必知道接收者的接口,也不必知道请求是怎么被接收的,以及操作是否被执行、是如何执行的,等等。
不过,命令模式也涉及创建型模式的一些特性:它涉及到具体命令类的创建以及如何和特定接收者实例相关联。每个具体的命令类都封装了特定的行为和调用接收者的相应方法。这样,每个命令可以看作是一个完整的操作。
因此,命令模式是创建型模式和行为型模式的交织。它是创建型的,因为它创建了具有特定行为的命令对象。同时它是行为型的,因为这些对象被调用来执行特定的行为。
总结
理解和从实际角度看待设计模式非常重要,因为它们是我们进行有效编程和设计灵活可维护系统的工具。希望这次的讨论能帮助你更好地理解命令模式,明白它是如何综合创建型模式和行为型模式的。
当然,设计模式并不是银弹,应当根据具体场景和需求采取合适的设计模式。而这个能力则需要大量实践和经验的积累,加油!
如果上面的内容对你有帮助,请点赞收藏哦,我会分享更多的经验~