C语言设计模式——命令模式

                                 C语言设计模式——命令模式

 

好处:让代码清晰明了,容易添加和删除,易维护。

哪些地方会用到命令模式?(列出几个常见的例子)

1、按键处理,每个按键按下得到一个索引(指的就是命令),一个按键对应一个处理函数。按键处理命令模式

2、协议解析(串口,网口,CAN,等等);以串口为例简单说明一下,比如有如下协议:http类型解析(html,jpg,jpeg...)

帧头命令数据长度数据内容校验帧尾
1字节1字节2字节n字节2字节1字节

命令1:0x01 温度

命令2:0x02 湿度

命令3:0x03 光照强度

传统的实现方式如下:(伪代码)

static uint8_t parse(char *buffer, uint16_t length)
{
  uint8_t head = buffer[0];
  uint8_t cmd = buffer[1];
  uint16_t len = (buffer[2] << 8) | buffer[3];
  uint16_t crc = CRCCheck(buffer, length - 3);
  uint8_t tail = buffer[length - 1];

  if((head != xxx) && (tail != xxx) && (crc != ((buffer[length - 3]) << 8) | buffer[length - 2]))
  {
    return 0;
  }

  switch(cmd)
  {
  case 0x01:
    int temperatue = *(int *)&buffer[4];
    printf("temperatue = %d\n", temperatue);
    break;
  case 0x02:
    int humidity = *(int *)&buffer[4];
    printf("humidity = %d\n", humidity);
    break;
  case 0x03:
    int illumination= *(int *)&buffer[4];
    printf("illumination = %d\n", illumination);
    break;
  default:
    printf("parse error\n");
    break;
  }

  return 1;
}

通过这段伪代码可以看出代码结构的一些问题,如果要添加更多的命令,势必需要向switch case语句中加入更多的case语句。使得解析函数越来越臃肿。当然我们可以使用如下方式规避一些问题:(伪代码)

// 当心字节对齐的问题
typedef struct
{
  uint8_t head;
  uint8_t cmd;
  uint16_t length;
  uint8_t data[1];
} package_t;

static int parse_temperature(char *buffer)
{
  int value = *(int *)buffer;
  printf("temperature = %d\n", value);
}
static int parse_humidity(char *buffer)
{
  int value = *(int *)buffer;
  printf("humidity = %d\n", value);
}

static int parse_illumination(char *buffer)
{
  int value = *(int *)buffer;
  printf("illumination = %d\n", value);
}

static uint8_t parse(char *buffer, uint16_t length)
{
  package_t *frame = (package_t *)buffer;
  uint16_t crc = CRCCheck(buffer, length - 3);
  uint8_t tail = buffer[length - 1];

  if((frame->head != xxx) && (tail != xxx) && (crc != (buffer[length - 3]) << 8 | buffer[length - 2]))
  {
    return 0;
  }

  switch(frame->cmd)
  {
  case 0x01:
    parse_temperature(frame->data);
    break;
  case 0x02:
    parse_humidity(frame->data);
    break;
  case 0x03:
    parse_illumination(frame->data);
    break;
  default:
    printf("parse error\n");
  }

  return 1;
}

相比于第一段代码,已经有了很大的改善,扩展性也得到了很大的提升。随着项目的进行,解析函数还是可能会越来越大。接下来就开始介绍命令模式。在命令模式里面,我们只需要维护一个命令列表就行了,而不需要关注解析函数本身。(伪代码)

// 当心字节对齐的问题
typedef struct
{
  uint8_t head;
  uint8_t cmd;
  uint16_t length;
  uint8_t data[1];
} package_t;

static int parse_temperature(char *buffer)
{
  int value = *(int *)buffer;
  printf("temperature = %d\n", value);
}
static int parse_humidity(char *buffer)
{
  int value = *(int *)buffer;
  printf("humidity = %d\n", value);
}

static int parse_illumination(char *buffer)
{
  int value = *(int *)buffer;
  printf("illumination = %d\n", value);
}

typedef struct
{
  uint8_t cmd;
  void (* handle)(char *buffer);
} package_entry_t;

static const package_entry_t package_items[] =
{
  {0x01, parse_temperature},
  {0x02, parse_humidity},
  {0x03, parse_illumination},
  {0xFF, NULL},
};

static uint8_t parse(char *buffer, uint16_t length)
{
  package_t *frame = (package_t *)buffer;
  uint16_t crc = CRCCheck(buffer, length - 3);
  uint8_t tail = buffer[length - 1];
  const package_entry_t *entry;

  if((frame->head != xxx) && (tail != xxx) && (crc != (buffer[length - 3]) << 8 | buffer[length - 2]))
  {
    return 0;
  }

  for(entry = package_items; entry->handle != NULL; ++entry)
  {
    if(frame->cmd == entry->cmd)
    {
      entry->handle(frame->data);
      break;
    }
  }

  return 1;
}

        我们可以看到,解析函数写好之后就不用动了,需要变化的只是一个表。这样写能让代码看起来干净整洁清晰,命令也可以使用宏定义或者枚举,看自己的喜好吧。一个命令对应一个处理函数,尽量使用此类方式去取代swicth case的方式,始终让代码保持整洁易扩展易维护的特性。

        上面使用了命令模式作用于串口协议,同样的方式可以适用于各种协议,网口协议的话,格式都不用改。如果是can协议的话,将can的id用作命令,就ok了。其他的,类似。

 

 

 

 

 

 

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值