C语言设计模式——命令模式
???????????????????????????????? C語言設(shè)計模式——命令模式
?
好處:讓代碼清晰明了,容易添加和刪除,易維護。
哪些地方會用到命令模式?(列出幾個常見的例子)
1、按鍵處理,每個按鍵按下得到一個索引(指的就是命令),一個按鍵對應(yīng)一個處理函數(shù)。按鍵處理命令模式
2、協(xié)議解析(串口,網(wǎng)口,CAN,等等);以串口為例簡單說明一下,比如有如下協(xié)議:http類型解析(html,jpg,jpeg...)
| 幀頭 | 命令 | 數(shù)據(jù)長度 | 數(shù)據(jù)內(nèi)容 | 校驗 | 幀尾 |
| 1字節(jié) | 1字節(jié) | 2字節(jié) | n字節(jié) | 2字節(jié) | 1字節(jié) |
命令1:0x01 溫度
命令2:0x02 濕度
命令3:0x03 光照強度
傳統(tǒng)的實現(xiàn)方式如下:(偽代碼)
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; }通過這段偽代碼可以看出代碼結(jié)構(gòu)的一些問題,如果要添加更多的命令,勢必需要向switch case語句中加入更多的case語句。使得解析函數(shù)越來越臃腫。當(dāng)然我們可以使用如下方式規(guī)避一些問題:(偽代碼)
// 當(dāng)心字節(jié)對齊的問題 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; }相比于第一段代碼,已經(jīng)有了很大的改善,擴展性也得到了很大的提升。隨著項目的進行,解析函數(shù)還是可能會越來越大。接下來就開始介紹命令模式。在命令模式里面,我們只需要維護一個命令列表就行了,而不需要關(guān)注解析函數(shù)本身。(偽代碼)
// 當(dāng)心字節(jié)對齊的問題 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; }??????? 我們可以看到,解析函數(shù)寫好之后就不用動了,需要變化的只是一個表。這樣寫能讓代碼看起來干凈整潔清晰,命令也可以使用宏定義或者枚舉,看自己的喜好吧。一個命令對應(yīng)一個處理函數(shù),盡量使用此類方式去取代swicth case的方式,始終讓代碼保持整潔易擴展易維護的特性。
??????? 上面使用了命令模式作用于串口協(xié)議,同樣的方式可以適用于各種協(xié)議,網(wǎng)口協(xié)議的話,格式都不用改。如果是can協(xié)議的話,將can的id用作命令,就ok了。其他的,類似。
?
?
?
?
?
?
?
總結(jié)
以上是生活随笔為你收集整理的C语言设计模式——命令模式的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux如何跳到命令行开端,Linux
- 下一篇: 每日一题(47)—— 置1与清0