2025/10/10日 每日总结 设计模式实践:命令模式实现可撤销/重做的加法计算器

设计模式实践:命令模式实现可撤销/重做的加法计算器

在需要支持操作回溯(撤销/重做)、日志记录或任务队列的场景中,直接耦合请求发送者和接收者会导致代码扩展性差。命令模式通过将“请求”封装为独立对象,实现请求发送者与接收者的解耦,同时支持对请求的存储、撤销和重做。本文以“支持多次撤销/重做的加法计算器”为例,分享命令模式的设计思想与实践应用。

一、命令模式核心思想

命令模式是一种行为型设计模式,核心目标是封装请求为对象,其关键结构包括:

  1. 抽象命令(Command):定义执行、撤销、重做的统一接口;

  2. 具体命令(Concrete Command):实现抽象命令接口,绑定接收者与具体请求逻辑;

  3. 接收者(Receiver):执行命令对应的具体业务逻辑(如加法运算);

  4. 调用者(Invoker):持有命令对象,负责触发命令执行、撤销或重做;

  5. 客户端(Client):创建命令对象并绑定接收者,将命令传递给调用者。
    本次实践中,加法计算器的核心需求是“记录操作历史,支持多次撤销和重做”。通过命令模式,每一次加法操作都被封装为命令对象,存储在历史记录中,撤销时反向执行命令,重做时重新执行命令。

二、类图设计

| AbstractCommand | // 抽象命令类
+-------------------+


- execute(value: int): int // 执行命令

- undo(): int // 撤销命令

- redo(): int // 重做命令
+-------------------+
^
|
+-------+-------+
| |
| ConcreteCommand| // 具体命令类
+---------------+


- adder: Adder // 接收者(加法器)

- value: int // 操作数值
+---------------+


- execute(value: int): int // 实现执行逻辑

- undo(): int // 实现撤销逻辑(加负数)

- redo(): int // 实现重做逻辑(重新加原数值)
+---------------+
+-------------------+
| Adder | // 接收者:加法运算核心逻辑
+-------------------+


- num: int // 当前计算结果
+-------------------+


- add(value: int): int // 加法运算,返回最新结果
+-------------------+
+-------------------+
| CalculatorForm | // 调用者:计算器表单(触发命令)
+-------------------+


- command: AbstractCommand // 持有命令对象
+-------------------+


- setCommand(command: AbstractCommand): void // 设置命令

- compute(value: int): void // 触发执行命令

- undo(): void // 触发撤销命令

- redo(): void // 触发重做命令
+-------------------+

三、完整代码实现

1. 接收者:Adder(加法器)

负责核心的加法运算,维护当前计算结果,不关心命令的撤销/重做逻辑。

/**
* 接收者类:加法器
* 实现核心加法运算,提供数值累加功能
*/
public class Adder {
private int num = 0; // 存储当前计算结果
/**
* 加法运算:将输入值累加到当前结果
* @param value 待加数值
* @return 累加后的结果
*/
public int add(int value) {
num += value;
return num;
}
}

2. 抽象命令:AbstractCommand

定义命令的统一接口,包含执行、撤销、重做三个核心方法,约束具体命令的行为。

/**
* 抽象命令类
* 定义命令的核心接口:执行、撤销、重做
*/
public abstract class AbstractCommand {
/**
* 执行命令
* @param value 操作数值
* @return 执行后的结果
*/
public abstract int execute(int value);
/**
* 撤销命令
* @return 撤销后的结果
*/
public abstract int undo();
/**
* 重做命令
* @return 重做后的结果
*/
public abstract int redo();
}

3. 具体命令:ConcreteCommand

实现抽象命令接口,绑定接收者(Adder),封装执行、撤销、重做的具体逻辑。

/**
* 具体命令类:加法命令
* 绑定加法器,实现加法操作的执行、撤销和重做
*/
public class ConcreteCommand extends AbstractCommand {
private Adder adder = new Adder(); // 持有接收者实例
private int value; // 存储当前执行的操作数值
/**
* 执行命令:调用加法器的加法运算
* @param value 待加数值
* @return 运算结果
*/
@Override
public int execute(int value) {
this.value = value; // 记录操作数值,用于后续撤销/重做
return adder.add(value);
}
/**
* 撤销命令:通过加负数抵消之前的加法操作
* @return 撤销后的结果
*/
@Override
public int undo() {
return adder.add(-value); // 原操作是+value,撤销则是-value
}
/**
* 重做命令:重新执行之前的加法操作
* @return 重做后的结果
*/
@Override
public int redo() {
return adder.add(value); // 再次执行+value操作
}
}

4. 调用者:CalculatorForm(计算器表单)

作为命令的触发者,持有命令对象,提供给客户端简单的操作接口(计算、撤销、重做),无需关心命令的具体实现。

/**
* 调用者类:计算器表单
* 提供用户操作接口,触发命令的执行、撤销和重做
*/
public class CalculatorForm {
private AbstractCommand command; // 持有命令对象
/**
* 设置命令对象(支持动态切换命令)
* @param command 具体命令实例
*/
public void setCommand(AbstractCommand command) {
this.command = command;
}
/**
* 触发计算操作(执行命令)
* @param value 待加数值
*/
public void compute(int value) {
int result = command.execute(value);
System.out.println("执行运算,运算结果为:" + result);
}
/**
* 触发撤销操作
*/
public void undo() {
int result = command.undo();
System.out.println("执行撤销,运算结果为:" + result);
}
/**
* 触发重做操作
*/
public void redo() {
int result = command.redo();
System.out.println("执行重做,运算结果为:" + result);
}
}

5. 客户端测试:Client

创建命令、调用者实例,绑定命令与调用者,模拟一系列加法、撤销、重做操作,验证命令模式的功能。

/**
* 客户端测试类:验证命令模式的撤销/重做功能
*/
public class Client {
public static void main(String[] args) {
// 1. 创建调用者(计算器表单)和具体命令
CalculatorForm calculator = new CalculatorForm();
AbstractCommand addCommand = new ConcreteCommand();
// 2. 绑定命令与调用者
calculator.setCommand(addCommand);
// 3. 模拟一系列操作
System.out.println("=== 开始执行加法运算 ===");
calculator.compute(10); // 执行:0 + 10 = 10
calculator.compute(5); // 执行:10 + 5 = 15
calculator.compute(10); // 执行:15 + 10 = 25
// 4. 测试撤销功能
System.out.println("\n=== 测试撤销操作 ===");
calculator.undo(); // 撤销:25 - 10 = 15
calculator.undo(); // 撤销:15 - 5 = 10
// 5. 测试重做功能
System.out.println("\n=== 测试重做操作 ===");
calculator.redo(); // 重做:10 + 5 = 15
calculator.redo(); // 重做:15 + 10 = 25
}
}

四、运行结果与验证

=== 开始执行加法运算 ===
执行运算,运算结果为:10
执行运算,运算结果为:15
执行运算,运算结果为:25
=== 测试撤销操作 ===
执行撤销,运算结果为:15
执行撤销,运算结果为:10
=== 测试重做操作 ===
执行重做,运算结果为:15
执行重做,运算结果为:25

结果分析:

  1. 执行逻辑:每次compute调用都会触发命令的execute方法,累加数值并记录操作;

  2. 撤销逻辑:undo方法通过加负数抵消上一次操作,实现数值回溯;

  3. 重做逻辑:redo方法重新执行上一次记录的操作,恢复数值;

  4. 解耦效果:客户端只需操作调用者(CalculatorForm),无需直接与加法器(Adder)或命令细节交互。

五、命令模式的核心优势与适用场景

核心优势

  1. 解耦请求发送者与接收者:调用者(计算器)无需知道接收者(加法器)的实现,只需通过命令对象间接交互;

  2. 支持撤销/重做:命令对象可记录操作状态,通过反向执行实现撤销,重新执行实现重做;

  3. 支持命令队列与日志:可将命令对象存储在集合中,实现批量执行、任务调度或操作日志记录;

  4. 扩展性强:新增命令(如减法、乘法)时,只需新增具体命令类,无需修改现有调用者和接收者代码。

适用场景

  1. 需支持撤销/重做的场景:如文本编辑器、计算器、图形设计软件;

  2. 需记录操作日志的场景:如数据库事务、操作审计系统;

  3. 需批量执行命令的场景:如任务调度、命令批处理;

  4. 需解耦请求发送者与接收者的场景:如GUI按钮与业务逻辑、远程命令调用。

六、命令模式的扩展优化

本次实现仅支持单次撤销/重做,若需支持多次撤销/重做,可通过“命令历史栈”优化具体命令类:

public class ConcreteCommand extends AbstractCommand {
private Adder adder = new Adder();
private Stack<Integer> valueStack = new Stack<>(); // 存储所有操作数值
private Stack<Integer> redoStack = new Stack<>(); // 存储撤销的操作数值
@Override
public int execute(int value) {
valueStack.push(value);
redoStack.clear(); // 执行新命令后,清空重做栈
return adder.add(value);
}
@Override
public int undo() {
if (valueStack.isEmpty()) return adder.num;
int lastValue = valueStack.pop();
redoStack.push(lastValue);
return adder.add(-lastValue);
}
@Override
public int redo() {
if (redoStack.isEmpty()) return adder.num;
int redoValue = redoStack.pop();
valueStack.push(redoValue);
return adder.add(redoValue);
}
}

通过栈结构存储操作历史,可支持无限次撤销/重做,更贴合实际应用场景(如文本编辑器的多次回溯)。

七、实践总结

  1. 命令模式的核心是“请求封装为对象”,将操作的发起者与执行者解耦,同时支持对操作的管理(撤销、重做、日志);

  2. 本次实践中,ConcreteCommand封装了加法操作,CalculatorForm作为调用者提供简单接口,Adder专注于核心运算,职责清晰;

  3. 命令模式适合需要“操作回溯”或“解耦请求与执行”的场景,是实现撤销/重做功能的首选模式;

  4. 实际开发中,命令模式的典型应用包括:Spring的JmsTemplate(消息命令封装)、GUI框架的按钮事件处理、数据库事务的提交/回滚。
    通过本次加法计算器的实践,我深刻理解了命令模式如何通过封装请求实现灵活的操作管理。合理运用命令模式,能让系统的扩展性和可维护性大幅提升,尤其在需要复杂操作管理的场景中表现突出。

posted @ 2025-12-29 14:37  Moonbeamsc  阅读(6)  评论(0)    收藏  举报
返回顶端