命令模式
约 643 字
命令模式
核心思想
命令模式将请求封装为一个对象,将发出者和处理者解耦,并能延迟请求执行或放入队列中,实现可撤销操作。
典型用例
撤销重做
命令模式可以用于实现撤销重做功能,每个执行的操作都是一个命令对象,可以被存储和恢复。
在这个例子中,创建了一个简单的文本编辑器和一些命令来修改文本。CommandInvoker 类负责执行命令并维护历史记录,以便支持撤销和重做功能。这样可以灵活地添加新的命令类型,同时保持编辑器类的简洁性和命令的独立性。
// npm run code src/code/design-pattern/command/undo-redo.ts
export {};
// 定义命令接口
interface Command {
execute(): void;
undo(): void;
}
// 创建接收者执行与请求相关操作
class TextEditor {
private content: string = '';
append(text: string): void {
this.content += text;
}
delete(): void {
this.content = this.content.slice(0, -1);
}
getContent(): string {
return this.content;
}
}
// 创建具体命令类,每个命令类将执行一个操作并保存其状态以便撤销。
class AppendCommand implements Command {
private editor: TextEditor;
private text: string;
constructor(editor: TextEditor, text: string) {
this.editor = editor;
this.text = text;
}
execute(): void {
this.editor.append(this.text);
}
undo(): void {
for (let i = 0; i < this.text.length; i++) {
this.editor.delete();
}
}
}
class DeleteCommand implements Command {
private editor: TextEditor;
private deleted: string = '';
constructor(editor: TextEditor) {
this.editor = editor;
}
execute(): void {
this.deleted = this.editor.getContent().slice(-1);
this.editor.delete();
}
undo(): void {
this.editor.append(this.deleted);
}
}
// 创建一个调用者类,用于执行命令。
// 并创建历史记录用于管理撤销和重做操作。
class CommandInvoker {
private history: Command[] = [];
private undoneCommands: Command[] = [];
executeCommand(command: Command): void {
command.execute();
this.history.push(command);
this.undoneCommands = []; // 清空撤销历史
}
undo(): void {
const command = this.history.pop();
if (command) {
command.undo();
this.undoneCommands.push(command);
}
}
redo(): void {
const command = this.undoneCommands.pop();
if (command) {
command.execute();
this.history.push(command);
}
}
}
const editor = new TextEditor();
const invoker = new CommandInvoker();
// 执行一些操作
invoker.executeCommand(new AppendCommand(editor, 'Hello'));
invoker.executeCommand(new AppendCommand(editor, ' World'));
console.log(editor.getContent()); // 输出: Hello World
// 撤销操作
invoker.undo();
console.log(editor.getContent()); // 输出: Hello
// 重做操作
invoker.redo();
console.log(editor.getContent()); // 输出: Hello World
处理UI交互
在图形用户界面应用中,命令模式通常被用于处理如按钮点击、菜单选择等用户交互动作。通过将这些动作封装成命令对象,可以更灵活地处理用户输入,使代码更易于管理和扩展。
// npm run code src/code/design-pattern/command/handling-ui-interactions.ts
export {};
// 命令接口
interface Command {
execute(): void;
}
// 接收者类
class Receiver {
performAction(action: string) {
console.log(`Executing action: ${action}`);
}
}
// 具体命令类
class ConcreteCommand implements Command {
private receiver: Receiver;
private action: string;
constructor(receiver: Receiver, action: string) {
this.receiver = receiver;
this.action = action;
}
execute(): void {
this.receiver.performAction(this.action);
}
}
// 调用者类
class Invoker {
private command!: Command;
setCommand(command: Command) {
this.command = command;
}
runCommand() {
this.command.execute();
}
}
// 客户端
class Client {
private invoker: Invoker;
private receiver: Receiver;
constructor() {
this.receiver = new Receiver();
this.invoker = new Invoker();
}
run() {
const command = new ConcreteCommand(this.receiver, "Save");
this.invoker.setCommand(command);
this.invoker.runCommand();
}
}
// 使用示例
const client = new Client();
client.run();
异步任务和队列
命令模式可以用来管理异步任务和队列,非常适合用于处理多线程应用程序中的后台任务。可以通过封装任务为命令对象,并将它们放入一个队列中,然后在后台线程或异步操作中执行这些任务。
// npm run code src/code/design-pattern/command/asynchronous-queues.ts
export {};
// 命令接口
interface Command {
execute(): Promise<void>;
}
// 具体命令类
class ConcreteCommand implements Command {
private payload: string;
constructor(payload: string) {
this.payload = payload;
}
async execute(): Promise<void> {
console.log(`Executing command with payload: ${this.payload}`);
// 模拟异步操作
await new Promise(resolve => setTimeout(resolve, 1000));
console.log(`Command with payload ${this.payload} executed`);
}
}
// 命令队列类
class CommandQueue {
private queue: Command[] = [];
addCommand(command: Command) {
this.queue.push(command);
}
async run(): Promise<void> {
while (this.queue.length > 0) {
const command = this.queue.shift();
if (command) {
await command.execute();
}
}
}
}
// 客户端
class Client {
private commandQueue: CommandQueue;
constructor() {
this.commandQueue = new CommandQueue();
}
addTask(payload: string) {
const command = new ConcreteCommand(payload);
this.commandQueue.addCommand(command);
}
runTasks() {
this.commandQueue.run();
}
}
// 使用示例
const client = new Client();
client.addTask("Task 1");
client.addTask("Task 2");
client.addTask("Task 3");
client.runTasks();
日志和事务系统
命令模式可以用来记录每个操作的历史,适合用来实现日志和事务系统,并在需要时重放这些操作,对于实现撤销/重做功能、事务回滚功能特别有用。
// npm run code src/code/design-pattern/command/transaction-systems.ts
export {};
// 命令接口
interface Command {
execute(): void;
undo(): void;
}
// 具体命令类
class ConcreteCommand implements Command {
private state: any;
constructor(private receiver: any, private action: string) {
this.state = receiver.state;
}
execute(): void {
console.log(`Executing action: ${this.action}`);
// 保存当前状态,然后执行操作
this.state = this.receiver.state;
this.receiver.performAction(this.action);
}
undo(): void {
console.log(`Undoing action: ${this.action}`);
// 撤销操作,恢复到之前的状态
this.receiver.state = this.state;
}
}
// 接收者类
class Receiver {
state: any;
performAction(action: string) {
this.state = action; // 举例修改状态
}
}
// 命令管理类
class CommandManager {
private history: Command[] = [];
executeCommand(command: Command) {
command.execute();
this.history.push(command);
}
undo() {
const command = this.history.pop();
if (command) {
command.undo();
}
}
}
// 客户端
class Client {
private commandManager: CommandManager;
private receiver: Receiver;
constructor() {
this.commandManager = new CommandManager();
this.receiver = new Receiver();
}
run() {
const command1 = new ConcreteCommand(this.receiver, "Action 1");
const command2 = new ConcreteCommand(this.receiver, "Action 2");
this.commandManager.executeCommand(command1);
this.commandManager.executeCommand(command2);
// 撤销最后一个操作
this.commandManager.undo();
}
}
// 使用示例
const client = new Client();
client.run();
远程过程调用(RPC)
命令模式可以用于封装远程过程调用,使远程调用的细节对客户端透明。在这个例子中,命令对象充当了客户端和远程服务之间的中间层,它封装了远程调用的所有细节。
// npm run code src/code/design-pattern/command/remote-procedure-call.ts
export {};
// 命令接口
interface Command {
execute(): Promise<void>;
}
// 远程服务类
class RemoteService {
async performAction(action: string): Promise<string> {
// 模拟远程服务调用
console.log(`Performing remote action: ${action}`);
return new Promise(resolve => setTimeout(() => resolve(`Result of ${action}`), 1000));
}
}
// 远程命令类
class RemoteCommand implements Command {
constructor(private service: RemoteService, private action: string) {}
async execute(): Promise<void> {
// 调用远程服务
const result = await this.service.performAction(this.action);
console.log(`Received result: ${result}`);
}
}
// 客户端
class Client {
private command: Command;
constructor(service: RemoteService, action: string) {
this.command = new RemoteCommand(service, action);
}
run() {
this.command.execute();
}
}
// 使用示例
const remoteService = new RemoteService();
const client = new Client(remoteService, "Action 1");
client.run();
宏命令
宏命令允许将多个命令封装成一个单一的命令,然后一次性执行这些命令。这在需要录制和重放一系列操作的场景(比如宏录制、批处理任务等)中非常有用。
// npm run code src/code/design-pattern/command/macrocommand.ts
export {};
// 命令接口
interface Command {
execute(): void;
}
// 具体命令类
class ConcreteCommand implements Command {
private payload: string;
constructor(payload: string) {
this.payload = payload;
}
execute(): void {
console.log(`Executing command: ${this.payload}`);
}
}
// 宏命令类
class MacroCommand implements Command {
private commands: Command[] = [];
add(command: Command) {
this.commands.push(command);
}
execute(): void {
this.commands.forEach(command => command.execute());
}
}
// 客户端
class Client {
private macroCommand: MacroCommand;
constructor() {
this.macroCommand = new MacroCommand();
}
addCommand(command: Command) {
this.macroCommand.add(command);
}
run() {
this.macroCommand.execute();
}
}
// 使用示例
const client = new Client();
client.addCommand(new ConcreteCommand("Action 1"));
client.addCommand(new ConcreteCommand("Action 2"));
client.addCommand(new ConcreteCommand("Action 3"));
client.run();