c语言设计模式代码完整实现-命令模式(command)

模式介绍:命令模式(command)

命令模式的解释如下:

向对象发送一个请求,但是并不知道该请求的具体接收者是谁,具体的处理过程是如何的,只知道在程序运行中指定具体的请求接收者即可,对于这样将请求封装成对象的我们称之为命令模式。所以命令模式将请求封装成对象,以便使用不同的请求、队列或者日志来参数化其他对象。同时命令模式支 持可撤销的操作。

              命令模式的C语言实现也是非常显性的。命令发送方不通过直接调用的方式,而是通过发一个命令消息给接收方,让接收方执行操作。C语言里采用命令模式的最常见的原因是核间通信,进程间交互。如果是核间通信,通常是把命令按协定的格式封装在消息数据包里。如果是进程间通信,通常封装成一个结构体,把参数带过去。命令的通道通常是队列。

命令模式实现

实现流程

C语言命令模式经典方式如下,和面向对象是有明显的不同的。下图的invoker表示发命令的实体,而handler表示执行命令的实体,这个和面向对象的命令模式里的含义不一样。

图表 1 C语言命令模式示意图

图表 2面向对象命令模式

C语言实现的命令模式核心数据结构是命令。发布命令的是invoker,多个invoker将命令封装起来,送到队列里。有一个函数或者线程称为receiver,检查队列里是否有没有处理的命令。由receiver负责调用各个handler。另外一个被经常使用的辅助数据结构是命令码数组,在如果invoker和handler运行于不同的环境,这种做法几乎是必选,如核间通信,内核和应用态通信。命令码作为索引,handler调用函数作为元素,Receiver根据不同的命令码调用handler。

也有不使用消息队列的C语言实现。

如果invoker和handler运行于相同的环境,可能直接把handler的回调函数的指针挂在命令结构体上,receiver可以直接调用handler的回调函数。很显然,不同的运行环境是没法这么做的。所以命令码数组是一个更为通用,封装性更好的方法。

              面向对象的命令模式并没有提及到命令的消息队列,也没有提及命令码数组。消息队列本身并不是命令模式的一部分,而是在C语言实现里经常会用到的,特别是命令和执行不再同一个运行环境。命令码数组对于面向对象来说可以用多个子类来实现,所以也不体现出来。

list.h  实现如下 c语言设计模式代码完整实现-基础(链表实现)

https://blog.csdn.net/jenie/article/details/106484602

 

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>

#include "list.h"

/*

向对象发送一个请求,但是并不知道该请求的具体接收者是谁,
具体的处理过程是如何的,只知道在程序运行中指定具体的请求接收者即可,
对于这样将请求封装成对象的我们称之为命令模式。所以命令模式将请求封装成对象,
以便使用不同的请求、队列或者日志来参数化其他对象。同时命令模式支 持可撤销的操作。
命令模式的C语言实现也是非常显性的。
命令发送方不通过直接调用的方式,而是通过发一个命令消息给接收方,
让接收方执行操作。C语言里采用命令模式的最常见的原因是核间通信,进程间交互。
如果是核间通信,通常是把命令按协定的格式封装在消息数据包里。
如果是进程间通信,通常封装成一个结构体,把参数带过去。命令的通道通常是队列。
*/


typedef enum
{
    CMD_1 = 0,
    CMD_2 = 1,

    CMD_MAX,
}E_COMMAND_MODE_INEX;

#define CMD_LEN 256
struct cmd_msg
{
    struct list_head list;
    int cmd_code;
    char buf[CMD_LEN]; //如果是不同环境的,只能用buffer数组,否则可以用指针
};

struct list_head command_global_list; //cmd_queue /cmd_list

//命令的实际处理函数
typedef int(*cmd_func)(char* buf);


struct cmd_handle
{
    int cmd_code;
    cmd_func handle_func;
};

int cmd1_handler(char* buf)
{
    printf("cmd1_handler.\n");
    return 0;
}

int cmd2_handler(char* buf)
{
    printf("cmd2_handler.\n");
    return 0;
}


/*
	命令码数组:
	命令码数组有两种方式,一种是将命令码作为数据的索引。
	另外一种情况是由于命令码太大,有一些特殊的规定,没法作为索引。
	所以在一个结构体里封装命令码和handler,最后实现一个结构体数据,
	这个在复杂的内核实现里会出现。
	下面是简单的命令码,就是函数指针数组。
*/

struct cmd_handle cmd_table[] =
        {
                { CMD_1, cmd1_handler },
                { CMD_2, cmd2_handler },
        };


/*
Invoker和receiver
Invoker的工作很简单,填充命令命令封装结构体,将其放入队列。
*/
/*
fill cmdcase and add cmd to queue
*/
int invoker(struct cmd_msg* pcmd_case)
{
    if (NULL == pcmd_case)
    {
        printf("fill cmdmsg error.\n");
    }
    //send cmd_case to queue
    list_add_tail(&pcmd_case->list, &command_global_list);
    printf("send cmd_case%d to queue.\n", pcmd_case->cmd_code);
    return 0;
}

#define cmd_queue_empty 0
#define cmd_queue_notempty 1

typedef enum
{
    CMD_QUEUE_EMPTY = 0,
    CMD_QUEUE_NOT_EMPTY,

    CMD_QUEUE_MAX

}E_CMD_QUEUE_STATUS;

/*
	思想是: 检查command_global_list中的queue是否为空,不为空就是获取里面的cmd_msg内容,然后根据cmd_code
	遍历cmd_table来handler它,处理完了记得要将cmd从列表中去掉
*/
E_CMD_QUEUE_STATUS cmd_receiver()
{
    struct cmd_msg *cmd_case;
    struct list_head* node;
    unsigned int index = 0;
    E_CMD_QUEUE_STATUS cmd_queue_status ;
    int is_handler = 0; //1 is handler

    //if (list_empty(command_global_list.next))
    if ((command_global_list.next) == NULL)
    {
        printf("list=0x%x.\n", &command_global_list);
        printf("pre_list=0x%x.\n", &command_global_list.prev);
        printf("next_list=0x%x.\n", &command_global_list.next);
        cmd_queue_status = CMD_QUEUE_EMPTY;
        return cmd_queue_status;
    }

    //get cmd_case from queue while queue is not empty
    node = command_global_list.next;
    cmd_case = list_entry(node, struct cmd_msg, list);
    for (index = 0; index < sizeof(cmd_table) / sizeof(cmd_table[0]); index++)
    {
        if (cmd_case->cmd_code == cmd_table[index].cmd_code)
        {
            cmd_table[index].handle_func(cmd_case->buf);
            is_handler = 1;
        }
    }

    if (is_handler)
    {
        printf("valid cmd%d.\n\n", cmd_case->cmd_code);
    }
    else
    {
        printf("invalid cmd%d.\n\n", cmd_case->cmd_code);
    }
    //delete data form queue.
    list_del(node);

    cmd_queue_status = CMD_QUEUE_NOT_EMPTY;
    return cmd_queue_status;

}

/*命令队列有很多形态,比如IPC通道,
用信号量,也能不要队列直接调用,
总之就是让命令交到reciever手上然后分发调用handler。
*/


int main()
{


    struct cmd_msg cmd1_case;
    struct cmd_msg cmd2_case;
    E_CMD_QUEUE_STATUS cmd_queue_status;

    //init queue
    init_list_head(&command_global_list);

    //fill command
    memset(&cmd1_case, 0, sizeof(cmd1_case));
    cmd1_case.cmd_code = CMD_1;
    invoker(&cmd1_case);

    memset(&cmd2_case, 0, sizeof(cmd2_case));
    cmd2_case.cmd_code = CMD_2;
    invoker(&cmd2_case);

    //处理cmd,这个处理在实际中可能在另一个线程中
    while (1)
    {
        cmd_queue_status = cmd_receiver();
        if (cmd_queue_status == CMD_QUEUE_EMPTY)
        {
            break;
        }
    }

    getchar();
    return 0;
}

模式实现总结

命令模式也是C语言实现的显性的设计模式,角色分为发布命令的invoker,分派命令的receiver和实际执行命令的handler。命令队列和命令码数组是核心的辅助元素。命令码数组目前只有两种类型。命令队列的实现类型就非常多,甚至未必是队列形式,需要设计人员根据经验把握。

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值