现在的位置: 首页 > 综合 > 正文

实现自己的“命令映射表”(上)

2014年02月08日 ⁄ 综合 ⁄ 共 1824字 ⁄ 字号 评论关闭

问题是这样的,在自己的一个程序里,需要根据接收到的命令(此命令是一个字串)来执行相应的函数。类似的情况可能有:

1、SOCKET 程序:程序可能要根据对方传来的命令来执行相应的函数。
2、脚本解析程序:在一个程序中根据读入的命令串来执行相应的函数。
3、类似WINDOWS的消息系统,一条消息对应一个函数。

反正这种的应用非常多,学会这种设计手法是非常有帮助的,因为它可以帮我们轻松的添加新的命令。这种手法也非常象MFC 中的消息映射表,那我们当然没必

要搞得那么复杂。只要能适合我们的应用就行。好,下面让我们开始吧。

我们想一下,一般碰到这种情况,我们会怎么做。我一般会这样写:

// 注意这里是类成员函数,示例代码仅供参考

// 读脚本
void CParser::GetCmd(CCommandList &CmdList)
{
 CString strCmd;
 while(!CmdList.End())
 {
  strCmd = CmdList.GetNextCmd();
  ExecCmd(strCmd);
 }
}

// 执行脚本
void CParser::ExecCmd(CString strCmd)
{
 if(strCmd == "#a")
  MethodA();
 if(strCmd == "#b")
  MethodB();
    ...
}

这种方法其实也没问题,只不过如果要增加新的命令就要在这个函数后面加,弄不好这个函数就会越来越长了(想一想WINDOWS成百上千的消息)。函数越来越长,

人就会看得好晕,好晕的时候就会出错,要知道,程序的错误大多数是由于人的失误造成的。所以下面是另一种可选的方法:

1、在类的头文件里定义一个map,把命令和函数对应起来,如下(省略了无关的代码)

#pragma warning(disable: 4786)
#include <map>
using namespace std;

class CParser
{
public:
 void GetCmd(CCommandList &CmdList);
 void ExecCmd(CString strCmd);

 // 响应命令表
 typedef void (CParser::*FuncPointer)(void);
 map<CString, FuncPointer> m_cmdMap;

 // 响应函数
 void MethodA();
 void MethodB();
};

2、在CPP文件里建表,注意写法
CParser::CParser()
{
 m_cmdMap["#a"] = (FuncPointer)&CParser::MethodA;
 m_cmdMap["#b"] = (FuncPointer)&CParser::MethodB;
}

在这里我们还可以学习MFC的做法,定义一个宏:
#define ON_CMD(a, b) ( m_cmdMap[(a)] = (FuncPointer)&CParser::b )

这样的话,我们的建表就更方便了,如下:
CParser::CParser()
{
 ON_CMD("#a", MethodA);
 ON_CMD("#b", MethodB);
}

3、修改一下ExecCmd

// 执行脚本
void CParser::ExecCmd(CString strCmd)
{
 if(m_cmdMap.find(strCmd) != m_cmdMap.end())
 {
  FuncPointer func;

  // 取得对应函数
  func = m_cmdMap[strCmd];

  // 请特别注意这里的写法!!!
  // 调用函数
  (this->*func)(); 
 }
 else
 {
  CString strMsg;
  strMsg.Format("找不到命令:/"%s/" 所对应的函数", strCmd);
  OutMsg(strMsg);
 }  
}

这样做我们就实现了自己的命令响应表了。

使用这种方法,好处如下:
1、可以代替上述的if-else、switch-case(例如WINDOWS程序)、虚函数(要知道,这种if-else是可以用多态来代替的)等方法。可参考《代替if-else,switch-case的几种方法》。
2、可以方便添加命令,添加时候不会影响原来的函数,符合OCP原则。
3、各个处理函数分开,不会互相影响,就算一个函数写错了,也不会“一只蚊子搞臭了一锅粥”,把错误的范围控制住了。

可能看到这里,有些人还要问如果参数不同怎么办?这个问题我们在下一篇再讨论。

 

 

抱歉!评论已关闭.