命令模式结合多态实现撤销与反撤销,核心是将每种操作封装为独立命令对象并统一实现execute()和undo()方法;多态确保调用者无需区分具体类型即可触发对应逻辑,接收者解耦、invoker维护历史栈,且命令须有状态、不可变、线程安全并控制内存。

命令模式结合多态实现撤销与反撤销,核心在于让每种操作都封装为独立的命令对象,并通过统一接口暴露 execute() 和 undo() 方法;而多态则确保调用者无需关心具体是哪种操作,只管触发对应方法即可。
命令接口定义与多态基础
先定义抽象命令接口,强制所有具体命令实现正向执行和逆向撤销逻辑:
- 接口中声明
execute()和undo()两个方法,不提供默认实现 - 每个具体命令类(如
AddTextCommand、DeleteTextCommand、MoveShapeCommand)继承该接口并各自实现——比如加文本的undo()就是删掉刚加的内容,删文本的undo()就是把删掉的部分还原回去 - 由于接口统一,调用者(如 Invoker)只需持有
Command类型引用,运行时自动绑定到实际子类方法,这就是多态在起作用
接收者解耦与状态快照
接收者(Receiver)负责真实操作,但不直接参与撤销逻辑决策:
- 接收者只暴露可执行的方法(如
add()、remove()、moveTo()),以及对应的逆向能力(如removeLast()、restore()、moveBack()) - 具体命令在构造时传入接收者,并在
execute()中调用其正向方法,在undo()中调用其逆向方法 - 关键点:撤销是否可靠,取决于接收者能否精确还原上一步状态——例如文本编辑器需保存被删文本内容,绘图程序需记录坐标与样式,而不是仅靠“再执行一次反向操作”这种粗略补偿
调用者维护操作历史栈
Invoker 不做业务判断,只管理命令生命周期:
- 使用
Stack<command></command>或List<command></command>存储已执行命令,每次execute()后压入栈顶 -
undo()时弹出栈顶命令并调用其undo()方法;若支持重做,还需额外维护一个redoStack,在undo()时把命令移入其中 - 多态在此体现得最明显:Invoker 调用
currentCommand.undo(),实际执行的是AddTextCommand.undo()还是ResizeImageCommand.undo(),完全由对象实际类型决定,代码零分支判断
避免常见陷阱
看似简单,但几个细节容易导致撤销失效:
- 命令对象必须是“有状态”的——比如删除操作要保存被删内容、位置、时间戳等,不能只存一个动作标识
- 不可变命令:一旦创建,其内部状态不应被后续操作修改,否则 undo 时可能读到错误数据
- 并发场景下,若多个线程共用同一 Invoker,需对命令栈加锁或改用线程安全集合(如
Collections.synchronizedList) - 内存控制:长期累积命令会占用大量内存,可设定最大历史步数,超出后自动丢弃最早命令











