設計模式的C語言應用-命令模式
來自專欄程序員之家12 人贊了文章
【小宅按】 模式介紹:命令模式(command)命令模式的解釋如下:向對象發送一個請求,但是並不知道該請求的具體接收者是誰,具體的處理過程是如何的,只知道在程序運行中指定具體的請求接收者即可,對於這樣將請求封裝成對象的我們稱之為命令模式。所以命令模式將請求封裝成對象,以便使用不同的請求、隊列或者日誌來參數化其他對象。同時命令模式支 持可撤銷的操作。 命令模式的C語言實現也是非常顯性的
模式介紹:命令模式(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語言實現里經常會用到的,特別是命令和執行不再同一個運行環境。命令碼數組對於面向對象來說可以用多個子類來實現,所以也不體現出來。命令模式的示例代碼以下代碼為偽碼。命令碼的定義#define CMD_1 0#define CMD_2 1#define CMD_MAX 2命令封裝結構體#define CMD_LEN 256struct cmd_msg{ int cmd_code; char buf[CMD_LEN];//如果是不同環境的,只能用buffer數組,否則可以用指針 }; 命令的實際處理函數
typedef int (*cmd_func)(char *buf);
int cmd1_handler(char *buf){ return 0;} int cmd2_handler(char *buf){ return 0;}
命令碼數組命令碼數組有兩種方式,一種是將命令碼作為數據的索引。另外一種情況是由於命令碼太大,有一些特殊的規定,沒法作為索引。所以在一個結構體里封裝命令碼和handler,最後實現一個結構體數據,這個在複雜的內核實現里會出現。 下面是簡單的命令碼,就是函數指針數組。cmd_func cmd_table[] ={ cmd1_handler, cmd2_handler, };Invoker和receiver
Invoker的工作很簡單,填充命令命令封裝結構體,將其放入隊列。 int invoker1(){ struct cmd_msg cmd1_case; memset(&cmd1_case, 0, sizeof(cmd1_case)); cmd1_case.cmd_code = CMD_1; //send cmd1_case to queuereturn 0;
} int invoker2(){ struct cmd_msg cmd1_case; memset(&cmd1_case, 0, sizeof(cmd1_case)); cmd1_case.cmd_code = CMD_2; //send cmd1_case to queue return 0;}
Receiver的工作就是監視命令隊列,取出命令調用handler。int cmd_receiver(){ struct cmd_msg *cmd_case; while(1) {//get cmd_case from queue while queue is not empty
(*cmd_table[cmd_case->cmd_code])(cmd_case->buf); } return 0;} 命令隊列有很多形態,比如IPC通道,用信號量,也能不要隊列直接調用,總之就是讓命令交到reciever手上然後分發調用handler。 偽碼main程序:int main(){ invoker1(); invoker2(); cmd_receiver(); return 0;} 內核的實現例子內核有非常多的例子,典型的是wireless extension的介面。上層應用通過ioctl下發命令到內核,內核解析後,調用相應的wireless extension內核側處理函數。這就是典型的不同運行環境的命令模式。參數是buffer,帶命令碼而不是直接發送函數指針。 /* -------------------------- IOCTL LIST -------------------------- */typedef int (*iw_handler)(struct net_device *dev, struct iw_request_info *info, void *wrqu, char *extra); /* Wireless Identification *///命令碼#define SIOCSIWCOMMIT 0x8B00 /* Commit pending changes to driver */#define SIOCGIWNAME 0x8B01 /* get name == wireless protocol */#define SIOCSIWNWID 0x8B02 /* set network id (pre-802.11) */#define SIOCGIWNWID 0x8B03 /* get network id (the cell) */ #define IW_HANDLER(id, func) [IW_IOCTL_IDX(id)] = func //命令碼數組static const iw_handler wl_handler[] ={ IW_HANDLER(SIOCSIWCOMMIT, (iw_handler) wireless_commit), IW_HANDLER(SIOCGIWNAME, (iw_handler) wireless_get_protocol), IW_HANDLER(SIOCSIWFREQ, (iw_handler) wireless_set_frequency), IW_HANDLER(SIOCGIWFREQ, (iw_handler) wireless_get_frequency), … } //典型的receiverstatic int ioctl_standard_iw_point(xxx){…{ /* Check need for ESSID compatibility for WE < 21 */ switch (cmd) { case SIOCSIWESSID: //沒法用索引,所以用了switch case case SIOCGIWESSID: case SIOCSIWNICKN: case SIOCGIWNICKN: if (iwp->length == descr->max_tokens + 1) essid_compat = 1; else if (IW_IS_SET(cmd) && (iwp->length != 0)) { char essid[IW_ESSID_MAX_SIZE + 1]; unsigned int len; len = iwp->length * descr->token_size; if (len > IW_ESSID_MAX_SIZE) return -EFAULT; err = copy_from_user(essid, iwp->pointer, len); if (err) return -EFAULT; if (essid[iwp->length - 1] == ) essid_compat = 1; } break; default: break; }… }可以看出,由於內核命令碼是有特別含義的,所以不能作為索引,只能receiver乾脆用switch case。在ioctl_standard_iw_point函數里就是用switch case。模式實現總結命令模式也是C語言實現的顯性的設計模式,角色分為發布命令的invoker,分派命令的receiver和實際執行命令的handler。命令隊列和命令碼數組是核心的輔助元素。命令碼數組目前只有兩種類型。命令隊列的實現類型就非常多,甚至未必是隊列形式,需要設計人員根據經驗把握。更多精彩內容,請滑至頂部點擊右上角關注小宅哦~
來源:華為雲社區 原文鏈接
推薦閱讀:
※理論上最好的編程語言: 並行因果篇
※6-17歲孩子學編程,為什麼學習這三種語言最合適?
※活見久,皮卡丘居然是一門編程語言?!
※Top分析/編程/ML工具,大家pick了誰? | KDnuggets2018年度調查
※來吧 主流編程語言圖譜 知識庫都在這了