面试官: 你好,今天我们要讨论的是命令模式。首先,你能解释一下什么是命令模式吗?
求职者: 当然可以。命令模式是一种行为设计模式,它将一个请求封装成一个对象,从而让你使用不同的请求、队列或者日志请求来参数化其他对象。命令模式也支持可撤销的操作。
面试官: 很好。那么,你能给我举一个命令模式的实际编程例子吗?
求职者: 当然可以。就像遥控器一样,它有很多按钮,每个按钮背后都有一个命令。在编程中,我们可以创建一个命令接口,这个接口定义了执行操作的方法。然后,我们可以为每个操作创建具体的命令类。这些类包含了执行操作所必需的信息和方法。
例如,在一个游戏模拟器中,我们可以有启动和停止游戏的命令。启动命令会调用游戏控制台的start_game
方法,而停止命令会调用stop_game
方法。
面试官: 那么,命令模式如何实现撤销和恢复操作呢?
求职者: 要实现撤销和恢复操作,命令对象需要存储原始状态信息,以便可以恢复到执行命令之前的状态。通常,每个命令类会有一个undo
方法来回滚操作。在执行操作时,命令对象被添加到历史记录中。当执行撤销操作时,可以从历史记录中获取最新的命令并调用其undo
方法。
面试官: 很好。那么,你认为命令模式的优点是什么?
求职者: 命令模式的优点包括:
- 解耦发送者和接收者:发送命令的对象不需要知道接收者是谁,也不需要知道被执行的操作的具体细节。
- 扩展性:可以很容易地添加新的命令,因为命令模式使用命令作为中间层。
- 组合命令:可以组合多个命令来实现宏命令。
- 支持撤销和恢复:命令模式可以通过实现
undo
方法来支持撤销和恢复操作。
面试官: 非常好。你提到了命令模式的关键优点。那么,命令模式是如何将创建型模式和行为型模式交织在一起的呢?
求职者: 命令模式可以看作是创建型模式和行为型模式的结合。从创建型模式的角度来看,命令模式涉及到创建具体命令对象,并与特定的接收者关联。而从行为型模式的角度来看,命令模式定义了一个执行操作的接口,使得发送者和接收者之间的请求调用可以解耦。
面试官: 很好,现在让我们来探讨一下命令模式如何支持宏命令的组合。首先,你能解释一下什么是宏命令吗?
求职者: 当然可以。宏命令是一组命令的集合,它可以一起被执行。在命令模式中,我们可以创建一个宏命令对象,这个对象内部包含了一组命令对象。当执行宏命令时,它会依次执行内部的每个命令。
面试官: 那你能用代码示例来说明如何实现宏命令吗?
求职者: 当然。我们可以创建一个MacroCommand
类,它实现了Command
接口。这个类内部维护了一个命令列表,并提供了添加命令的方法。在execute
方法中,它会遍历并执行所有的命令。
class MacroCommand implements Command { private List<Command> commands; public MacroCommand() { commands = new ArrayList<>(); } public void addCommand(Command command) { commands.add(command); } public void execute() { for (Command command : commands) { command.execute(); } } public void undo() { // Optional: Implement undo in reverse order if needed ListIterator<Command> iterator = commands.listIterator(commands.size()); while (iterator.hasPrevious()) { iterator.previous().undo(); } } }
在这个例子中,MacroCommand
可以包含任何数量的命令对象。当调用它的execute
方法时,它会执行所有添加的命令。我们还可以实现undo
方法,以便可以撤销宏命令中的所有操作。
面试官: 现在,我们来看一个具体的例子,说明如何在命令模式中实现命令的撤销功能。你能给我一个简单的代码示例吗?
求职者: 当然可以。假设我们有一个简单的文本编辑器,我们可以添加文本和删除文本。我们将创建一个添加文本的命令,并实现一个撤销功能,这样我们就可以撤销添加的文本
首先,我们定义一个Command
接口,它包含execute
和undo
方法:
interface Command { void execute(); void undo(); }
然后,我们创建一个AddTextCommand
类,它实现了Command
接口
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); } }
接下来,我们定义TextEditor
类,它包含添加和删除文本的功能:
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 String getText() { return text.toString(); } }
现在,我们需要一个机制来执行和撤销命令,我们创建一个TextEditorDriver
类:
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(); } } }
最后,我们在main
方法中模拟命令的执行和撤销:
public class Main { public static void main(String[] args) { TextEditor editor = new TextEditor(); TextEditorDriver driver = new TextEditorDriver(); Command addTextCmd = new AddTextCommand("Hello, World!", editor); driver.executeCommand(addTextCmd); System.out.println("Text after add command: " + editor.getText()); driver.undoLastCommand(); System.out.println("Text after undo: " + editor.getText()); } }
在这个例子中,我们添加了文本"Hello, World!"到文本编辑器,然后我们执行了撤销操作,这将移除我们刚刚添加的文本。
面试官: 好的,让我们离开文本编辑器,看看命令模式中撤销功能在其他场景中的应用。你能给我一个不同的例子吗?
求职者: 当然。让我们考虑一个家居自动化系统,比如智能灯泡的控制。我们可以开灯和关灯,并且我们希望能够撤销这些操作。
首先,我们定义一个Command
接口:
interface Command { void execute(); void undo(); }
接着,我们创建Light
类和两个命令:LightOnCommand
和LightOffCommand
:
class Light { private boolean isOn = false; public void toggleLight() { isOn = !isOn; if (isOn) { System.out.println("Light is ON"); } else { System.out.println("Light is OFF"); } } } class LightOnCommand implements Command { private Light light; public LightOnCommand(Light light) { this.light = light; } public void execute() { light.toggleLight(); } public void undo() { light.toggleLight(); } } class LightOffCommand implements Command { private Light light; public LightOffCommand(Light light) { this.light = light; } public void execute() { light.toggleLight(); } public void undo() { light.toggleLight(); } }
然后,我们创建一个控制器,它可以执行命令并且支持撤销:
class RemoteControl { private Command lastCommand; public void submit(Command command) { command.execute(); lastCommand = command; } public void undoLastCommand() { if (lastCommand != null) { lastCommand.undo(); } } }
最后,我们在main
方法中模拟命令的执行和撤销
public class SmartHomeDemo { public static void main(String[] args) { Light livingRoomLight = new Light(); Command lightOn = new LightOnCommand(livingRoomLight); Command lightOff = new LightOffCommand(livingRoomLight); RemoteControl remoteControl = new RemoteControl(); remoteControl.submit(lightOn); // Light is ON remoteControl.submit(lightOff); // Light is OFF // Oops, didn't mean to turn it off. Let's undo that. remoteControl.undoLastCommand(); // Light is ON } }
在这个例子中,我们通过RemoteControl
执行了开灯和关灯的命令,并且使用撤销功能来撤销关灯的操作。
面试官: 很好,这个例子清楚地展示了命令模式在家居自动化系统中的撤销功能。你展示了命令模式的灵活性和命令撤销的实用性。这就是我们今天要讨论的全部内容,谢谢你的参与。