文章目录
【Java设计模式】命令模式:增强灵活的命令执行
一、概述
在Java中,命令模式是一种行为设计模式,它将请求封装为对象,允许对客户端进行参数化,包括队列、请求和操作。该模式还支持可撤销的操作,增强了管理和执行命令的灵活性。
二、命令设计模式的别名
- Action(动作)
- Transaction(事务)
三、命令设计模式的意图
命令设计模式是Java编程中使用的一种行为模式。它将请求封装为对象,允许对客户端进行参数化,包括队列、请求和操作。此模式还支持可撤销的操作,增强了命令执行的灵活性。
四、命令模式的详细解释及实际示例
- 实际示例:
- 想象一个智能家居系统,您可以通过中央应用程序控制灯光、恒温器和安全摄像头等设备。每个操作这些设备的命令都被封装为一个对象,使系统能够排队、顺序执行和在必要时撤销命令。这种方法将控制逻辑与设备实现解耦,允许在不改变核心应用程序的情况下轻松添加新设备或功能。这种灵活性和功能说明了命令设计模式在Java编程中的实际应用。
- 通俗解释:
- 将请求存储为命令对象允许在以后执行或撤销该操作。
- 维基百科解释:
- 在面向对象编程中,命令模式是一种行为设计模式,其中一个对象用于封装在以后的时间执行操作或触发事件所需的所有信息。
五、Java中命令模式的编程示例
在命令模式中,对象用于封装在以后的时间执行操作或触发事件所需的所有信息。此模式对于在应用程序中实现撤销功能特别有用。
在我们的示例中,一个“巫师”对一个“哥布林”施放法术。每个法术都是一个命令对象,可以执行和撤销,展示了Java中命令模式的核心原则。法术一个接一个地在哥布林身上执行。第一个法术使哥布林缩小,第二个法术使他隐形。然后巫师一个接一个地逆转法术。这里的每个法术都是一个可以撤销的命令对象。
让我们从“巫师”类开始。
@Slf4j
public class Wizard {
private final Deque<Runnable> undoStack = new LinkedList<>();
private final Deque<Runnable> redoStack = new LinkedList<>();
public Wizard() {
}
public void castSpell(Runnable runnable) {
runnable.run();
undoStack.offerLast(runnable);
}
public void undoLastSpell() {
if (!undoStack.isEmpty()) {
var previousSpell = undoStack.pollLast();
redoStack.offerLast(previousSpell);
previousSpell.run();
}
}
public void redoLastSpell() {
if (!redoStack.isEmpty()) {
var previousSpell = redoStack.pollLast();
undoStack.offerLast(previousSpell);
previousSpell.run();
}
}
@Override
public String toString() {
return "Wizard";
}
}
接下来,我们有“哥布林”,他是法术的“目标”。
@Slf4j
@Getter
@Setter
public abstract class Target {
private Size size;
private Visibility visibility;
public void printStatus() {
LOGGER.info("{}, [size={}] [visibility={}]", this, getSize(), getVisibility());
}
public void changeSize() {
var oldSize = getSize() == Size.NORMAL? Size.SMALL : Size.NORMAL;
setSize(oldSize);
}
public void changeVisibility() {
var visible = getVisibility() == Visibility.INVISIBLE? Visibility.VISIBLE : Visibility.INVISIBLE;
setVisibility(visible);
}
}
public class Goblin extends Target {
public Goblin() {
setSize(Size.NORMAL);
setVisibility(Visibility.VISIBLE);
}
@Override
public String toString() {
return "Goblin";
}
}
最后,我们可以展示“巫师”施放法术的完整示例。
public static void main(String[] args) {
var wizard = new Wizard();
var goblin = new Goblin();
goblin.printStatus();
wizard.castSpell(goblin::changeSize);
goblin.printStatus();
wizard.castSpell(goblin::changeVisibility);
goblin.printStatus();
wizard.undoLastSpell();
goblin.printStatus();
wizard.undoLastSpell();
goblin.printStatus();
wizard.redoLastSpell();
goblin.printStatus();
wizard.redoLastSpell();
goblin.printStatus();
}
以下是程序输出:
20:13:38.406 [main] INFO com.iluwatar.command.Target -- Goblin, [size=normal] [visibility=visible]
20:13:38.409 [main] INFO com.iluwatar.command.Target -- Goblin, [size=small] [visibility=visible]
20:13:38.409 [main] INFO com.iluwatar.command.Target -- Goblin, [size=small] [visibility=invisible]
20:13:38.409 [main] INFO com.iluwatar.command.Target -- Goblin, [size=small] [visibility=visible]
20:13:38.409 [main] INFO com.iluwatar.command.Target -- Goblin, [size=normal] [visibility=visible]
20:13:38.409 [main] INFO com.iluwatar.command.Target -- Goblin, [size=small] [visibility=visible]
20:13:38.409 [main] INFO com.iluwatar.command.Target -- Goblin, [size=small] [visibility=invisible]
六、何时在Java中使用命令模式
当您需要用操作参数化对象、支持撤销操作、或围绕基于原始操作构建的高级操作构建系统时,命令设计模式是适用的。它通常用于GUI按钮、数据库事务和宏记录。
当您想要:
- 用要执行的操作参数化对象,为过程语言中发现的回调提供面向对象的替代方案。命令可以注册并稍后执行。
- 在不同时间指定、排队和执行请求,允许命令独立于原始请求存在,甚至可以跨进程传输。
- 支持撤销功能,其中命令的执行操作存储状态并包括一个撤销操作来逆转先前的操作。这通过维护历史列表允许无限的撤销和重做功能。
- 记录更改以在系统崩溃后重新应用它们。通过向命令接口添加加载和存储操作,您可以维护更改的持久日志,并通过重新加载和重新执行此日志中的命令来恢复。
- 围绕基于原始操作构建的高级操作构建系统,这在基于事务的系统中很常见。命令模式通过提供调用和扩展操作的公共接口来模拟事务。
- 保留请求的历史记录。
- 实现回调功能。
- 实现撤销功能。
七、命令模式在Java中的实际应用
- 桌面应用程序中的GUI按钮和菜单项。
- 支持回滚的数据库系统和事务系统中的操作。
- 文本编辑器和电子表格等应用程序中的宏记录。
- java.lang.Runnable
- org.junit.runners.model.Statement
- Netflix Hystrix
- javax.swing.Action
八、命令模式的优点和权衡
优点:
- 将调用操作的对象与知道如何执行操作的对象解耦。
- 很容易添加新的命令,因为您不必更改现有的类。
- 您可以将一组命令组合成一个复合命令。
权衡:
- 为每个单独的命令增加了类的数量。
- 通过在发送者和接收者之间添加多个层,可能会使设计复杂化。
九、源码下载
通过本文的介绍,相信大家对Java中的命令模式有了更深入的了解。在实际开发中,合理运用命令模式可以提高代码的灵活性和可扩展性,但需要注意其增加的类数量和可能带来的设计复杂性。