模式介绍:命令模式(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 queuereturn 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 casecase 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] == '\0')essid_compat = 1;}break;default:break;}…}

可以看出,由于内核命令码是有特别含义的,所以不能作为索引,只能receiver干脆用switch case。在ioctl_standard_iw_point函数里就是用switch case。

模式实现总结

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

来源:华为云社区  作者:lurayvis

设计模式的C语言应用-命令模式-第五章相关推荐

  1. 设计模式的C语言应用-建造者模式-第七章

    模式介绍 建造者模式将复杂产品的构建过程封装分解在不同的方法中,使得创建过程非常清晰.它隔离了复杂产品 对象的创建和使用,使得相同的创建过程能够创建不同的产品.若几个 产品之间存在较大的差异,则不适用 ...

  2. Java设计模式之行为型:命令模式

    前言: 在开发中,我们可能需要向某些对象发送一些请求,但我们不知道请求的具体接收者是谁,也不知道被请求的操作是哪个,只知道在系统运行中指定具体的请求接收者即可,打个比方,电视遥控器,我们只需知道按哪个 ...

  3. 设计模式自学笔记007_Real(命令模式、备忘录模式、桥接模式)

    设计模式自学笔记007_Real(命令模式.备忘录模式.桥接模式) 一.命令模式 在软件设计的过程中,我们经常需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道请求的操作是哪个.我们只需要 ...

  4. 设计模式(一):命令模式(2)——命令模式扩展之宏命令

    前言 命令模式的讲解分为四篇: 设计模式(一):命令模式(1)--基本的命令模式 设计模式(一):命令模式(2)--命令模式扩展之宏命令 设计模式(一):命令模式(3)--命令模式扩展之队列请求 设计 ...

  5. R语言实战笔记--第十五章 处理缺失数据

    R语言实战笔记–第十五章 处理缺失数据 标签(空格分隔): R语言 处理缺失数据 VIM mice 缺失值(NA),是导致我们计算错误的一大来源,处理缺失数据在实际的应用中有着较为重要的作用. 基本方 ...

  6. 设计模式笔记之十四 (命令模式)

    命令模式 命令模式是一种比较容易理解的设计模式,顾名思义,就是调用者发一个命令,有人给做完就行了,无关乎怎么做,也无关乎谁做. 我们继续我们以往的方式:理论联系实践的方式来理解命令模式. 最近我们实验 ...

  7. 设计模式(十三): 命令模式

    命令模式 命令模式(Command Pattern)是一种数据驱动的设计模式,它属于行为型模式.请求以命令的形式包裹在对象中,并传给调用对象.调用对象寻找可以处理该命令的合适的对象,并把该命令传给相应 ...

  8. 围观设计模式(23)--行为型之命令模式(Command Pattern)

    在面向对象程式设计的范畴中,命令模式是一种设计模式,它尝试以物件来代表实际行动.命令物件可以把行动(action) 及其参数封装起来,于是这些行动可以被: 重复多次 取消(如果该物件有实作的话) 取消 ...

  9. 【HeadFirst 设计模式学习笔记】6 命令模式

    1.这一节我们的任务是创建一个类似智能家居的万能遥控器,控制各种家电.我们需要将"请求"封装成对象(一个命令对象通过在特定接收者上绑定一组动作来封装请求),以便使用不同的请求.队列 ...

最新文章

  1. iOS显示gif图片的几种方法
  2. mysql设置php权限_MYSQL新建用户并设置权限
  3. 2020年 第11届 蓝桥杯 第2次模拟赛真题详解及小结【Java版】
  4. 转行,转向哪里?电子工程师!!!
  5. html 属性中嵌套php,如何在PHP中使用嵌套数组创建HTML数据属性字符串?
  6. LeetCode 1060. 有序数组中的缺失元素(二分查找)
  7. iOS实现简书的账号识别方式(正则表达式)
  8. oracle存储过程id递增,oracle存储过程——按id更新相关信息
  9. xilinx基础篇Ⅰ(7)ISE14.7开发基础流程 [CPLD章节]
  10. 计算机操作系统第四版知识点总结(详细版一)
  11. 相对湿度与绝对湿度转换表包含负温度
  12. 牛客网之黑暗的字符串
  13. 将旧笔记本的硬盘DIY成移动硬盘
  14. phpcms v9 栏目伪静态完全自定义为栏目英文目录名
  15. 材料研发转行,转什么比较好
  16. 为什么要配环境变量 ? 环境变量是个啥 ?
  17. openCV中watershed的使用
  18. XP 小技巧( 隐藏文件、mp3转换WMA )
  19. 致曾经的老游戏天下霸图1——重写天下霸图计划
  20. SweetAlert swal 是同步的还是异步的

热门文章

  1. go json数据出现unicode_【Android】OkHttp、Retrofit拿到的json是Unicode,我要的是UTF-8呀...
  2. html恢复安卓版,recovery恢复模式 进入Recovery模式前
  3. roobo机器人怎么唱歌_日本推出机器人“妻子”,拥有3大功能,能替代真人伴侣吗?...
  4. mongodb 存储过程 遍历表数据_一个mongodb存储过程
  5. 拔刀剑服务器r87修复版,我的世界拔刀剑mod刀剑修复教程
  6. d3.js(v5.7)树状图
  7. ansible之二:模块用法
  8. VC++ 获得程序运行根目录 汇编源码
  9. be my friend
  10. aspnet管理员用户登录_WINDOWS/LINUX系统修改管理员密码方法