命令模式核心解析
- 命令模式 (Command Pattern):
- 将「请求」封装成一个独立的对象 (命令对象),这个对象包含执行请求的所有信息 (比如接收者、执行方法、参数等),从而实现请求发送者 (调用者) 和请求执行者 (接收者) 的解耦;
- 可以用一个生动的比喻理解:
- (调用者 / Invoker) 去餐厅点餐,只需要告诉服务员 (命令对象) “来一份宫保鸡丁”,不用自己去厨房做菜;
- 服务员拿着点菜单 (命令),把需求传达给厨师 (接收者 / Receiver);
- 厨师负责实际做菜 (执行命令);
- 整个过程中,你 (调用者) 不用知道厨师是谁、怎么做菜,只需要和命令对象交互,实现了 “点餐请求” 和 “做菜执行” 的解耦;
- 类图:
- 设计代码:
// ====================== 1. Command 命令接口(对应类图的Command) ====================== /** * 点餐命令接口:所有菜品命令的统一规范 */ interface Command { // 执行命令(对应“做菜”动作) execute(): void; } // ====================== 2. Receiver 接收者(对应类图的Chef) ====================== /** * 厨师:真正执行“做菜”的角色(接收者) */ class Chef { /** * 做宫保鸡丁(具体业务逻辑) */ public cookKungPaoChicken(): void { console.log("🍲 厨师:开始做宫保鸡丁,配料:鸡肉、花生米、干辣椒..."); console.log("🍲 厨师:宫保鸡丁做好啦!"); } /** * 做鱼香肉丝(具体业务逻辑) */ public cookYuXiangShreddedPork(): void { console.log("🍜 厨师:开始做鱼香肉丝,配料:猪肉、木耳、胡萝卜..."); console.log("🍜 厨师:鱼香肉丝做好啦!"); } } // ====================== 3. ConcreteCommand 具体命令(对应类图的两个具体命令) ====================== /** * 宫保鸡丁命令:绑定厨师,实现执行逻辑 */ class KungPaoChickenCommand implements Command { // 持有接收者(厨师) private chef: Chef; constructor(chef: Chef) { this.chef = chef; } // 执行命令:调用厨师的“做宫保鸡丁”方法 execute(): void { console.log("📝 服务员:收到宫保鸡丁订单,通知厨师制作..."); this.chef.cookKungPaoChicken(); } } /** * 鱼香肉丝命令:绑定厨师,实现执行逻辑 */ class YuXiangShreddedPorkCommand implements Command { // 持有接收者(厨师) private chef: Chef; constructor(chef: Chef) { this.chef = chef; } // 执行命令:调用厨师的“做鱼香肉丝”方法 execute(): void { console.log("📝 服务员:收到鱼香肉丝订单,通知厨师制作..."); this.chef.cookYuXiangShreddedPork(); } } // ====================== 4. Invoker 调用者(对应类图的Customer) ====================== /** * 顾客:发起点餐请求的角色(调用者) */ class Customer { // 持有当前订单命令 private orderCommand?: Command; /** * 下单:设置要执行的命令 * @param command 具体的菜品命令 */ public setOrder(command: Command): void { this.orderCommand = command; console.log("👤 顾客:我要下单啦!"); } /** * 催单/确认下单:执行命令 */ public placeOrder(): void { if (this.orderCommand) { console.log("👤 顾客:麻烦快点上菜~"); this.orderCommand.execute(); } else { console.log("👤 顾客:还没点单呢!"); } } } // ====================== 5. 测试运行(模拟餐厅点餐流程) ====================== // 1. 创建接收者:厨师 const chef = new Chef(); // 2. 创建具体命令:宫保鸡丁、鱼香肉丝订单 const kungPaoCommand = new KungPaoChickenCommand(chef); const yuXiangCommand = new YuXiangShreddedPorkCommand(chef); // 3. 创建调用者:顾客 const customer = new Customer(); // 4. 模拟点餐流程 console.log("===== 第一次点餐:宫保鸡丁 ====="); customer.setOrder(kungPaoCommand); customer.placeOrder(); console.log("\n===== 第二次点餐:鱼香肉丝 ====="); customer.setOrder(yuXiangCommand); customer.placeOrder(); console.log("\n===== 未点单直接催单 ====="); const emptyCustomer = new Customer(); emptyCustomer.placeOrder();
常用使用场景
编辑器类操作(最典型)
-
场景:富文本编辑器、代码编辑器的撤销 / 重做、剪切 / 复制 / 粘贴操作;
-
实现代码:
// 1. 命令接口 interface EditorCommand { execute(): void; } // 2. 接收者:富文本编辑器核心逻辑 class RichTextEditor { private content: string = "Hello World"; // 加粗文本 public bold(): void { this.content = `**${this.content}**`; console.log(`[加粗后] ${this.content}`); } // 斜体文本 public italic(): void { this.content = `*${this.content}*`; console.log(`[斜体后] ${this.content}`); } public getContent() { return this.content; } } // 3. 具体命令:加粗命令 class BoldCommand implements EditorCommand { private editor: RichTextEditor; constructor(editor: RichTextEditor) { this.editor = editor; } execute(): void { this.editor.bold(); } } // 4. 具体命令:斜体命令 class ItalicCommand implements EditorCommand { private editor: RichTextEditor; constructor(editor: RichTextEditor) { this.editor = editor; } execute(): void { this.editor.italic(); } } // 5. 调用者:编辑器工具栏 class EditorToolbar { private currentCommand?: EditorCommand; // 设置并执行命令 public setCommand(command: EditorCommand): void { this.currentCommand = command; this.currentCommand.execute(); } } // 测试代码 const editor = new RichTextEditor(); const toolbar = new EditorToolbar(); console.log("===== 编辑器操作演示 ====="); toolbar.setCommand(new BoldCommand(editor)); // 输出:[加粗后] **Hello World** toolbar.setCommand(new ItalicCommand(editor)); // 输出:[斜体后] ***Hello World***
按钮 / 菜单与业务逻辑解耦
-
场景:后台管理系统的操作按钮(新增 / 编辑 / 删除);
-
实现代码:
// 1. 命令接口 interface ActionCommand { execute(): Promise<void>; // 异步执行(模拟接口请求) } // 2. 接收者:用户管理业务逻辑 class UserService { // 新增用户(模拟接口请求) public async addUser(username: string): Promise<void> { return new Promise(resolve => { setTimeout(() => { console.log(`[业务逻辑] 新增用户:${username}(模拟接口请求成功)`); resolve(); }, 500); }); } // 删除用户(模拟接口请求) public async deleteUser(id: number): Promise<void> { return new Promise(resolve => { setTimeout(() => { console.log(`[业务逻辑] 删除用户:ID=${id}(模拟接口请求成功)`); resolve(); }, 500); }); } } // 3. 具体命令:新增用户命令 class AddUserCommand implements ActionCommand { private userService: UserService; private username: string; constructor(userService: UserService, username: string) { this.userService = userService; this.username = username; } async execute(): Promise<void> { await this.userService.addUser(this.username); } } // 4. 具体命令:删除用户命令 class DeleteUserCommand implements ActionCommand { private userService: UserService; private userId: number; constructor(userService: UserService, userId: number) { this.userService = userService; this.userId = userId; } async execute(): Promise<void> { await this.userService.deleteUser(this.userId); } } // 5. 调用者:按钮组件(视图层) class Button { private command?: ActionCommand; private label: string; constructor(label: string) { this.label = label; } // 绑定命令 public setCommand(command: ActionCommand): void { this.command = command; } // 模拟按钮点击 public async onClick(): Promise<void> { console.log(`[视图层] 点击【${this.label}】按钮`); if (this.command) { await this.command.execute(); } else { console.log(`[视图层] 【${this.label}】按钮未绑定命令`); } } } // 测试代码 const userService = new UserService(); // 创建按钮 const addUserBtn = new Button("新增用户"); const deleteUserBtn = new Button("删除用户"); // 绑定命令 addUserBtn.setCommand(new AddUserCommand(userService, "zhangsan")); deleteUserBtn.setCommand(new DeleteUserCommand(userService, 1001)); // 模拟点击 console.log("===== 按钮解耦演示 ====="); await addUserBtn.onClick(); // 输出: // [视图层] 点击【新增用户】按钮 // [业务逻辑] 新增用户:zhangsan(模拟接口请求成功) await deleteUserBtn.onClick(); // 输出: // [视图层] 点击【删除用户】按钮 // [业务逻辑] 删除用户:ID=1001(模拟接口请求成功)
命令模式的优势
-
解耦:调用者和接收者完全解耦;
-
扩展性:新增命令无需修改现有代码;
-
组合性:支持宏命令和命令队列;
-
可撤销:轻松实现撤销/重做功能;
-
历史记录:可以记录操作日志;
组合模式(结构型模式)
上一篇