基于typescript严格模式以实现undo和redo功能为目标的命令模式代码参考
下面是一个完整的、严格模式下的 TypeScript 实现,包含 CommandManager、Command 和 CompositeCommand 类,支持 undo/redo 功能。
完整实现代码
1. Command 接口和基类
// src/commands/ICommand.ts
export interface ICommand {execute(): void;undo(): void;redo(): void;
}
// src/commands/Command.ts
import { ICommand } from "./ICommand";export abstract class Command implements ICommand {private _isExecuted: boolean = false;public execute(): void {if (!this._isExecuted) {this.doExecute();this._isExecuted = true;}}public undo(): void {if (this._isExecuted) {this.doUndo();this._isExecuted = false;}}public redo(): void {if (!this._isExecuted) {this.doExecute();this._isExecuted = true;}}protected abstract doExecute(): void;protected abstract doUndo(): void;public get isExecuted(): boolean {return this._isExecuted;}
}
2. CompositeCommand 实现
// src/commands/CompositeCommand.ts
import { ICommand } from "./ICommand";
import { Command } from "./Command";export class CompositeCommand extends Command {private readonly _commands: ICommand[] = [];public addCommand(command: ICommand): void {this._commands.push(command);}public removeCommand(command: ICommand): void {const index = this._commands.indexOf(command);if (index !== -1) {this._commands.splice(index, 1);}}public clearCommands(): void {this._commands.length = 0;}public get commands(): ReadonlyArray<ICommand> {return this._commands;}protected doExecute(): void {for (const command of this._commands) {command.execute();}}protected doUndo(): void {// Undo in reverse orderfor (let i = this._commands.length - 1; i >= 0; i--) {this._commands[i].undo();}}
}
3. CommandManager 实现
// src/commands/CommandManager.ts
import { ICommand } from "./ICommand";export class CommandManager {private readonly _undoStack: ICommand[] = [];private readonly _redoStack: ICommand[] = [];private readonly _maxStackSize: number;constructor(maxStackSize: number = 100) {this._maxStackSize = maxStackSize;}public execute(command: ICommand): void {command.execute();this._undoStack.push(command);// Clear redo stack when executing a new commandthis._redoStack.length = 0;// Ensure we don't exceed max stack sizeif (this._undoStack.length > this._maxStackSize) {this._undoStack.shift();}}public undo(): boolean {const command = this._undoStack.pop();if (command) {command.undo();this._redoStack.push(command);return true;}return false;}public redo(): boolean {const command = this._redoStack.pop();if (command) {command.redo();this._undoStack.push(command);return true;}return false;}public clearHistory(): void {this._undoStack.length = 0;this._redoStack.length = 0;}public get canUndo(): boolean {return this._undoStack.length > 0;}public get canRedo(): boolean {return this._redoStack.length > 0;}public get undoStackSize(): number {return this._undoStack.length;}public get redoStackSize(): number {return this._redoStack.length;}
}
示例使用代码
// src/main.ts
import { CommandManager } from "./commands/CommandManager";
import { Command } from "./commands/Command";
import { CompositeCommand } from "./commands/CompositeCommand";// 示例命令 - 修改文本
class ChangeTextCommand extends Command {constructor(private _target: { text: string },private _newText: string,private _oldText: string) {super();}protected doExecute(): void {this._target.text = this._newText;console.log(`Text changed to: ${this._newText}`);}protected doUndo(): void {this._target.text = this._oldText;console.log(`Text reverted to: ${this._oldText}`);}
}// 示例命令 - 修改数字
class ChangeNumberCommand extends Command {constructor(private _target: { value: number },private _newValue: number,private _oldValue: number) {super();}protected doExecute(): void {this._target.value = this._newValue;console.log(`Number changed to: ${this._newValue}`);}protected doUndo(): void {this._target.value = this._oldValue;console.log(`Number reverted to: ${this._oldValue}`);}
}// 使用示例
const commandManager = new CommandManager();const textObject = { text: "Initial text" };
const numberObject = { value: 0 };// 创建并执行单个命令
const changeTextCommand = new ChangeTextCommand(textObject, "New text", textObject.text);
commandManager.execute(changeTextCommand);// 创建并执行组合命令
const compositeCommand = new CompositeCommand();
compositeCommand.addCommand(new ChangeTextCommand(textObject, "Composite text", textObject.text));
compositeCommand.addCommand(new ChangeNumberCommand(numberObject, 42, numberObject.value));
commandManager.execute(compositeCommand);// 测试 undo/redo
console.log("--- Undo ---");
commandManager.undo(); // 撤销组合命令
console.log("Current text:", textObject.text);
console.log("Current number:", numberObject.value);console.log("--- Redo ---");
commandManager.redo(); // 重做组合命令
console.log("Current text:", textObject.text);
console.log("Current number:", numberObject.value);console.log("--- Undo single command ---");
commandManager.undo(); // 撤销组合命令
commandManager.undo(); // 撤销第一个文本修改命令
console.log("Current text:", textObject.text);
严格模式配置
确保你的 tsconfig.json
包含严格模式设置:
{"compilerOptions": {"strict": true,"noImplicitAny": true,"strictNullChecks": true,"strictFunctionTypes": true,"strictBindCallApply": true,"strictPropertyInitialization": true,"noImplicitThis": true,"alwaysStrict": true}
}
设计说明
ICommand 接口:定义了命令模式的基本操作
Command 抽象类:
实现了基础执行状态跟踪
要求子类实现实际执行和撤销逻辑
CompositeCommand:
可以组合多个命令一起执行
撤销时按相反顺序执行
CommandManager:
管理 undo/redo 堆栈
限制最大堆栈大小防止内存问题
提供清晰的API和状态查询
这个实现是完全类型安全的,符合 TypeScript 严格模式要求,并且可以直接集成到 Vite 项目中。