設計模式的C語言應用-命令模式

設計模式的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 256

struct 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 queue

return 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),

}

//典型的receiver

static 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年度調查
來吧 主流編程語言圖譜 知識庫都在這了

TAG:編程語言 | CC | C編程語言 |