命令模式指南:将请求封装成对象的灵活方案

命令模式指南:将请求封装成对象的灵活方案

📖 你有没有遇到过这些问题?

想象一下这些生活场景:

场景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:工作任务分配

复制代码
直接操作:老板直接告诉员工具体怎么做
命令模式:老板下达任务单,员工按任务单执行

命令模式的价值

  1. 解耦请求者和接收者:发送者不需要知道接收者的具体实现
  2. 支持撤销操作:命令对象可以保存状态,支持undo/redo
  3. 支持队列和日志:命令可以排队执行,记录操作历史
  4. 支持宏命令:多个命令可以组合成复合命令

🌟 命令模式基本结构

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;
}

📚 参考资料

设计模式

  1. Command Pattern - 命令模式详解
  2. Gang of Four Design Patterns - 经典设计模式
  3. Behavioral Patterns - 行为型模式
  4. Undo/Redo Implementation - 撤销重做实现

嵌入式应用

  1. Embedded Command Pattern - Linux内核编码风格
  2. Menu System Design - GitHub开源编码规范
  3. Event Handling Patterns - 事件处理模式
  4. State Management - FreeRTOS官方文档

🏷️ 总结

命令模式就像智能遥控器

  • 封装请求让操作标准化和可复用
  • 解耦发送者让系统更加灵活
  • 支持撤销让操作更加安全
  • 支持队列让处理更加有序

核心原则

  1. 封装请求 > 直接调用
  2. 支持撤销 > 不可逆操作
  3. 队列处理 > 立即执行
  4. 宏命令 > 单一操作

记住这个公式

复制代码
优秀的命令模式 = 封装请求 + 支持撤销 + 队列处理 + 宏命令

通过本文的学习,我们了解了命令模式的原理和最佳实践,掌握了构建灵活请求处理系统的方法。


命令模式是请求处理的指挥官,让你的代码像军队一样执行有序!

相关推荐
yujkss2 小时前
23种设计模式之【命令模式】-核心原理与 Java 实践
设计模式
vker3 小时前
第 4 天:建造者模式(Builder Pattern)—— 创建型模式
java·后端·设计模式
我最厉害。,。4 小时前
代理通讯篇&无外网或不可达&SockS全协议&规则配置&C2正反向上线&解决方案
c语言
new_daimond4 小时前
设计模式详解:单例模式、工厂方法模式、抽象工厂模式
单例模式·设计模式·工厂方法模式
疯狂的Alex5 小时前
string::c_str()写入导致段错误?const指针的只读特性与正确用法
c语言·开发语言
那个指针是空的?5 小时前
无痛c到c++
c语言·开发语言·c++
bkspiderx5 小时前
C++设计模式之创建型模式:原型模式(Prototype)
c++·设计模式·原型模式
KWTXX10 小时前
【国二】C语言-部分典型真题
java·c语言·算法
phdsky11 小时前
【设计模式】中介者模式
c++·设计模式·中介者模式