命令模式指南:将请求封装成对象的灵活方案
📖 你有没有遇到过这些问题?
想象一下这些生活场景:
场景1:遥控器操作
方式A:每个按钮直接连接到电视的具体功能,换个品牌就不能用
方式B:遥控器发送标准命令,电视根据命令执行对应操作
哪种方式更灵活?
场景2:餐厅点餐方式A:顾客直接跑到厨房告诉厨师要做什么菜
方式B:顾客写订单,服务员传递给厨师,厨师按订单制作
哪种方式更有序?
在编程中,命令模式就像遥控器和餐厅订单一样重要!
直接调用像顾客直接找厨师一样混乱:
c
// ❌ 直接调用,紧耦合
void handle_key_press(uint8_t key_id)
{
switch (key_id)
{
case KEY_UP:
// 直接操作菜单
if (current_menu_index > 0)
{
current_menu_index--;
update_display();
save_menu_state();
}
break;
case KEY_DOWN:
// 直接操作菜单
if (current_menu_index < max_menu_items - 1)
{
current_menu_index++;
update_display();
save_menu_state();
}
break;
case KEY_ENTER:
// 直接执行操作
execute_menu_item(current_menu_index);
break;
}
}
命令模式像标准化订单一样清晰:
c
// ✅ 命令模式,解耦灵活
typedef struct Command Command_t;
struct Command {
bool (*execute)(Command_t *self);
bool (*undo)(Command_t *self);
void (*cleanup)(Command_t *self);
const char *name;
void *data;
};
// 按键处理器
void handle_key_press(uint8_t key_id)
{
Command_t *command = KeyCommandFactory_Create(key_id);
if (command)
{
CommandInvoker_Execute(command);
CommandHistory_Add(command);
}
}
// 菜单向上命令
Command_t* create_menu_up_command(void)
{
return MenuUpCommand_Create();
}
本文将详细介绍命令模式的原理和最佳实践,帮助开发者构建灵活的请求处理系统。
🎯 为什么需要命令模式?
生活中的例子
场景1:银行转账
直接操作:客户直接操作银行系统数据库
命令模式:客户填写转账单,银行按单据执行操作
场景2:工作任务分配
直接操作:老板直接告诉员工具体怎么做
命令模式:老板下达任务单,员工按任务单执行
命令模式的价值
- 解耦请求者和接收者:发送者不需要知道接收者的具体实现
- 支持撤销操作:命令对象可以保存状态,支持undo/redo
- 支持队列和日志:命令可以排队执行,记录操作历史
- 支持宏命令:多个命令可以组合成复合命令
🌟 命令模式基本结构
1. 核心组件
命令接口和具体命令
c
// command.h - 命令模式核心定义
typedef struct Command Command_t;
typedef struct CommandInvoker CommandInvoker_t;
// 命令接口
struct Command {
bool (*execute)(Command_t *self);
bool (*undo)(Command_t *self);
void (*cleanup)(Command_t *self);
const char *name;
void *receiver;
void *parameters;
};
// 命令调用者
struct CommandInvoker {
Command_t **command_history;
size_t history_size;
size_t history_capacity;
int current_position;
};
// 命令工厂接口
typedef Command_t* (*CommandFactory_t)(void *params);
// 基础命令操作
bool Command_Execute(Command_t *command);
bool Command_Undo(Command_t *command);
void Command_Cleanup(Command_t *command);
// 命令调用者操作
bool CommandInvoker_Init(CommandInvoker_t *invoker, size_t capacity);
bool CommandInvoker_Execute(CommandInvoker_t *invoker, Command_t *command);
bool CommandInvoker_Undo(CommandInvoker_t *invoker);
bool CommandInvoker_Redo(CommandInvoker_t *invoker);
void CommandInvoker_Cleanup(CommandInvoker_t *invoker);
2. 基础实现
命令模式核心实现
c
// command.c - 命令模式实现
#include "command.h"
#include <stdlib.h>
#include <string.h>
/**
* @brief 执行命令
* @param command 命令对象
* @return 执行结果
*/
bool Command_Execute(Command_t *command)
{
if (command == NULL || command->execute == NULL)
{
return false;
}
return command->execute(command);
}
/**
* @brief 撤销命令
* @param command 命令对象
* @return 撤销结果
*/
bool Command_Undo(Command_t *command)
{
if (command == NULL || command->undo == NULL)
{
return false;
}
return command->undo(command);
}
/**
* @brief 清理命令
* @param command 命令对象
*/
void Command_Cleanup(Command_t *command)
{
if (command == NULL)
{
return;
}
if (command->cleanup)
{
command->cleanup(command);
}
free(command);
}
/**
* @brief 初始化命令调用者
* @param invoker 调用者对象
* @param capacity 历史记录容量
* @return 初始化结果
*/
bool CommandInvoker_Init(CommandInvoker_t *invoker, size_t capacity)
{
if (invoker == NULL || capacity == 0)
{
return false;
}
invoker->command_history = malloc(sizeof(Command_t*) * capacity);
if (invoker->command_history == NULL)
{
return false;
}
invoker->history_size = 0;
invoker->history_capacity = capacity;
invoker->current_position = -1;
return true;
}
/**
* @brief 执行命令并记录历史
* @param invoker 调用者对象
* @param command 命令对象
* @return 执行结果
*/
bool CommandInvoker_Execute(CommandInvoker_t *invoker, Command_t *command)
{
if (invoker == NULL || command == NULL)
{
return false;
}
// 执行命令
if (!Command_Execute(command))
{
return false;
}
// 清理当前位置之后的命令(支持分支undo/redo)
for (int i = invoker->current_position + 1; i < invoker->history_size; i++)
{
Command_Cleanup(invoker->command_history[i]);
}
// 添加到历史记录
invoker->current_position++;
if (invoker->current_position >= invoker->history_capacity)
{
// 历史记录满了,移除最早的命令
Command_Cleanup(invoker->command_history[0]);
memmove(invoker->command_history,
&invoker->command_history[1],
sizeof(Command_t*) * (invoker->history_capacity - 1));
invoker->current_position = invoker->history_capacity - 1;
}
invoker->command_history[invoker->current_position] = command;
invoker->history_size = invoker->current_position + 1;
printf("执行命令: %s\n", command->name ? command->name : "未知命令");
return true;
}
/**
* @brief 撤销上一个命令
* @param invoker 调用者对象
* @return 撤销结果
*/
bool CommandInvoker_Undo(CommandInvoker_t *invoker)
{
if (invoker == NULL || invoker->current_position < 0)
{
return false;
}
Command_t *command = invoker->command_history[invoker->current_position];
if (Command_Undo(command))
{
invoker->current_position--;
printf("撤销命令: %s\n", command->name ? command->name : "未知命令");
return true;
}
return false;
}
/**
* @brief 重做下一个命令
* @param invoker 调用者对象
* @return 重做结果
*/
bool CommandInvoker_Redo(CommandInvoker_t *invoker)
{
if (invoker == NULL ||
invoker->current_position + 1 >= invoker->history_size)
{
return false;
}
invoker->current_position++;
Command_t *command = invoker->command_history[invoker->current_position];
if (Command_Execute(command))
{
printf("重做命令: %s\n", command->name ? command->name : "未知命令");
return true;
}
else
{
invoker->current_position--;
return false;
}
}
🎨 实际应用场景
1. LFS菜单系统命令
菜单操作命令
c
// menu_commands.h - 菜单命令定义
typedef struct {
int *current_index;
int previous_index;
int max_items;
} MenuNavigationData_t;
typedef struct {
int menu_item_index;
void *menu_context;
} MenuActionData_t;
// 菜单命令创建函数
Command_t* MenuUpCommand_Create(int *current_index, int max_items);
Command_t* MenuDownCommand_Create(int *current_index, int max_items);
Command_t* MenuEnterCommand_Create(int menu_item_index, void *context);
Command_t* MenuBackCommand_Create(void);
具体菜单命令实现
c
// menu_up_command.c - 菜单向上命令
static bool menu_up_execute(Command_t *self)
{
MenuNavigationData_t *data = (MenuNavigationData_t*)self->parameters;
if (data == NULL || data->current_index == NULL)
{
return false;
}
// 保存当前状态用于撤销
data->previous_index = *data->current_index;
// 执行向上操作
if (*data->current_index > 0)
{
(*data->current_index)--;
MenuDisplay_Update(*data->current_index);
printf("菜单向上移动到: %d\n", *data->current_index);
return true;
}
return false; // 已经在顶部
}
static bool menu_up_undo(Command_t *self)
{
MenuNavigationData_t *data = (MenuNavigationData_t*)self->parameters;
if (data == NULL || data->current_index == NULL)
{
return false;
}
// 恢复之前的状态
*data->current_index = data->previous_index;
MenuDisplay_Update(*data->current_index);
printf("撤销菜单向上,恢复到: %d\n", *data->current_index);
return true;
}
static void menu_up_cleanup(Command_t *self)
{
if (self && self->parameters)
{
free(self->parameters);
self->parameters = NULL;
}
}
Command_t* MenuUpCommand_Create(int *current_index, int max_items)
{
Command_t *command = malloc(sizeof(Command_t));
if (command == NULL)
{
return NULL;
}
MenuNavigationData_t *data = malloc(sizeof(MenuNavigationData_t));
if (data == NULL)
{
free(command);
return NULL;
}
data->current_index = current_index;
data->previous_index = *current_index;
data->max_items = max_items;
command->execute = menu_up_execute;
command->undo = menu_up_undo;
command->cleanup = menu_up_cleanup;
command->name = "菜单向上";
command->receiver = NULL;
command->parameters = data;
return command;
}
// menu_down_command.c - 菜单向下命令
static bool menu_down_execute(Command_t *self)
{
MenuNavigationData_t *data = (MenuNavigationData_t*)self->parameters;
if (data == NULL || data->current_index == NULL)
{
return false;
}
data->previous_index = *data->current_index;
if (*data->current_index < data->max_items - 1)
{
(*data->current_index)++;
MenuDisplay_Update(*data->current_index);
printf("菜单向下移动到: %d\n", *data->current_index);
return true;
}
return false; // 已经在底部
}
static bool menu_down_undo(Command_t *self)
{
MenuNavigationData_t *data = (MenuNavigationData_t*)self->parameters;
if (data == NULL || data->current_index == NULL)
{
return false;
}
*data->current_index = data->previous_index;
MenuDisplay_Update(*data->current_index);
printf("撤销菜单向下,恢复到: %d\n", *data->current_index);
return true;
}
Command_t* MenuDownCommand_Create(int *current_index, int max_items)
{
Command_t *command = malloc(sizeof(Command_t));
if (command == NULL)
{
return NULL;
}
MenuNavigationData_t *data = malloc(sizeof(MenuNavigationData_t));
if (data == NULL)
{
free(command);
return NULL;
}
data->current_index = current_index;
data->previous_index = *current_index;
data->max_items = max_items;
command->execute = menu_down_execute;
command->undo = menu_down_undo;
command->cleanup = menu_up_cleanup; // 共用清理函数
command->name = "菜单向下";
command->receiver = NULL;
command->parameters = data;
return command;
}
2. 参数设置命令
数值修改命令
c
// value_change_command.c - 数值修改命令
typedef struct {
float *target_value;
float old_value;
float new_value;
float min_value;
float max_value;
bool (*validator)(float value);
void (*callback)(float value);
} ValueChangeData_t;
static bool value_change_execute(Command_t *self)
{
ValueChangeData_t *data = (ValueChangeData_t*)self->parameters;
if (data == NULL || data->target_value == NULL)
{
return false;
}
// 验证新值
if (data->new_value < data->min_value || data->new_value > data->max_value)
{
printf("数值超出范围: %.2f (范围: %.2f - %.2f)\n",
data->new_value, data->min_value, data->max_value);
return false;
}
if (data->validator && !data->validator(data->new_value))
{
printf("数值验证失败: %.2f\n", data->new_value);
return false;
}
// 保存旧值
data->old_value = *data->target_value;
// 设置新值
*data->target_value = data->new_value;
// 调用回调函数
if (data->callback)
{
data->callback(data->new_value);
}
printf("参数修改: %.2f -> %.2f\n", data->old_value, data->new_value);
return true;
}
static bool value_change_undo(Command_t *self)
{
ValueChangeData_t *data = (ValueChangeData_t*)self->parameters;
if (data == NULL || data->target_value == NULL)
{
return false;
}
// 恢复旧值
*data->target_value = data->old_value;
// 调用回调函数
if (data->callback)
{
data->callback(data->old_value);
}
printf("撤销参数修改: %.2f -> %.2f\n", data->new_value, data->old_value);
return true;
}
static void value_change_cleanup(Command_t *self)
{
if (self && self->parameters)
{
free(self->parameters);
self->parameters = NULL;
}
}
Command_t* ValueChangeCommand_Create(float *target, float new_value,
float min_val, float max_val,
bool (*validator)(float),
void (*callback)(float))
{
Command_t *command = malloc(sizeof(Command_t));
if (command == NULL)
{
return NULL;
}
ValueChangeData_t *data = malloc(sizeof(ValueChangeData_t));
if (data == NULL)
{
free(command);
return NULL;
}
data->target_value = target;
data->old_value = *target;
data->new_value = new_value;
data->min_value = min_val;
data->max_value = max_val;
data->validator = validator;
data->callback = callback;
command->execute = value_change_execute;
command->undo = value_change_undo;
command->cleanup = value_change_cleanup;
command->name = "参数修改";
command->receiver = NULL;
command->parameters = data;
return command;
}
3. 宏命令(复合命令)
组合多个命令
c
// macro_command.c - 宏命令实现
typedef struct {
Command_t **commands;
size_t command_count;
size_t executed_count; // 用于部分撤销
} MacroCommandData_t;
static bool macro_command_execute(Command_t *self)
{
MacroCommandData_t *data = (MacroCommandData_t*)self->parameters;
if (data == NULL || data->commands == NULL)
{
return false;
}
data->executed_count = 0;
// 依次执行所有命令
for (size_t i = 0; i < data->command_count; i++)
{
if (Command_Execute(data->commands[i]))
{
data->executed_count++;
}
else
{
// 执行失败,撤销已执行的命令
printf("宏命令执行失败,撤销已执行的 %zu 个命令\n", data->executed_count);
for (int j = data->executed_count - 1; j >= 0; j--)
{
Command_Undo(data->commands[j]);
}
data->executed_count = 0;
return false;
}
}
printf("宏命令执行成功,共执行 %zu 个命令\n", data->executed_count);
return true;
}
static bool macro_command_undo(Command_t *self)
{
MacroCommandData_t *data = (MacroCommandData_t*)self->parameters;
if (data == NULL || data->commands == NULL)
{
return false;
}
// 逆序撤销所有已执行的命令
for (int i = data->executed_count - 1; i >= 0; i--)
{
if (!Command_Undo(data->commands[i]))
{
printf("宏命令撤销失败,命令索引: %d\n", i);
return false;
}
}
printf("宏命令撤销成功,共撤销 %zu 个命令\n", data->executed_count);
data->executed_count = 0;
return true;
}
static void macro_command_cleanup(Command_t *self)
{
MacroCommandData_t *data = (MacroCommandData_t*)self->parameters;
if (data)
{
if (data->commands)
{
// 清理所有子命令
for (size_t i = 0; i < data->command_count; i++)
{
Command_Cleanup(data->commands[i]);
}
free(data->commands);
}
free(data);
self->parameters = NULL;
}
}
Command_t* MacroCommand_Create(Command_t **commands, size_t count)
{
if (commands == NULL || count == 0)
{
return NULL;
}
Command_t *command = malloc(sizeof(Command_t));
if (command == NULL)
{
return NULL;
}
MacroCommandData_t *data = malloc(sizeof(MacroCommandData_t));
if (data == NULL)
{
free(command);
return NULL;
}
data->commands = malloc(sizeof(Command_t*) * count);
if (data->commands == NULL)
{
free(data);
free(command);
return NULL;
}
// 复制命令指针
memcpy(data->commands, commands, sizeof(Command_t*) * count);
data->command_count = count;
data->executed_count = 0;
command->execute = macro_command_execute;
command->undo = macro_command_undo;
command->cleanup = macro_command_cleanup;
command->name = "宏命令";
command->receiver = NULL;
command->parameters = data;
return command;
}
📚 参考资料
设计模式
- Command Pattern - 命令模式详解
- Gang of Four Design Patterns - 经典设计模式
- Behavioral Patterns - 行为型模式
- Undo/Redo Implementation - 撤销重做实现
嵌入式应用
- Embedded Command Pattern - Linux内核编码风格
- Menu System Design - GitHub开源编码规范
- Event Handling Patterns - 事件处理模式
- State Management - FreeRTOS官方文档
🏷️ 总结
命令模式就像智能遥控器:
- 封装请求让操作标准化和可复用
- 解耦发送者让系统更加灵活
- 支持撤销让操作更加安全
- 支持队列让处理更加有序
核心原则:
- 封装请求 > 直接调用
- 支持撤销 > 不可逆操作
- 队列处理 > 立即执行
- 宏命令 > 单一操作
记住这个公式:
优秀的命令模式 = 封装请求 + 支持撤销 + 队列处理 + 宏命令
通过本文的学习,我们了解了命令模式的原理和最佳实践,掌握了构建灵活请求处理系统的方法。
命令模式是请求处理的指挥官,让你的代码像军队一样执行有序! ⚡