代码重构 - 表驱动和工厂模式结合

我目前负责的模块File Browser是专门管理网络文件的软件,用户对于远程电脑文件的处理都是通过发送PDU进行的。因为支持的功能繁多,所以PDU的类型也很多,由于PDU处理机制过程比较复杂,而老的处理方法又很不灵活,导致添加新的PDU类型,以及对现有PDU类的改动比较麻烦,有牵一发而动全身的感觉。所以有必要对此机制加以优化改良。
我们先看看PDU接受的过程:Fiel Browser ARM层得到数据包,根据其类型创建相应的PDU对象,然后再将此对象作为参数传送给相应的派发函数,最终由交给统一的处理函数。如下图:

CFsRemoteView::DataIndication

CFsRemoteView:HandleFileOfferPdu

CFsRemoteView:HandleFileAcceptPdu

。。。

CFsApp:: OnCommand

阅读一下相关的函数代码实现:
int CFsRemoteView:: DataIndication(short priority, LPBYTE lpData, DWORD dwLength)

。。。
 
CDvMemArchive ar((LPBYTE)lpData, dwLength);
WORD wType;
ar >> wType;
 CDvPdu* pPdu = NULL;
 switch ((int)wType)
 {
 case FS_PDU_FILE_OFFER:
  WBXLOG_SP(
CFsRemoteView:: DataIndication:FS_PDU_FILE_OFFER
);
  pPdu = new CFsFileOfferPdu;
  pPdu->SerializeFrom(ar);
HandleFileOfferPdu((CFsFileOfferPdu*)pPdu);      
break;

  case FS_PDU_FILE_ACCEPT:
  WBXLOG_SP(
CFsRemoteView:: DataIndication:FS_PDU_FILE_ACCEPT
);
  pPdu = new CFsFileAcceptPdu;
  pPdu->SerializeFrom(ar);
  HandleFileAcceptPdu((CFsFileAcceptPdu*)pPdu);
  break;
。。。

}

  if (pPdu)
   delete pPdu;
  return 0;
}

void CFsRemoteView::HandleFileOfferPdu(CFsFileOfferPdu* pPdu)
{
 m_pCmdTarget->OnCommand(NID_RV_FILE_OFFER, (LPARAM)pPdu);
}
void CFsRemoteView::HandleFileAcceptPdu(CFsFileAcceptPdu* pPdu)
{
 m_nFlowControl = TRUE;
    m_pCmdTarget->OnCommand(NID_RV_FILE_ACCEPT, (LPARAM)pPdu);
}//void CFsRemoteView::HandleFileAcceptPdu(CFsFileAcceptPdu* pPdu)
。。。

可以看到CFsRemoteView:: DataIndication函数采用了switch case这种常被人所垢病的语句,由于PDU Class的数目很多(30)。所以整个函数代码非常的多(超过300),复杂度也比较大(43),难以阅读理解,而且针对每个PDU类都要有相应的成员函数进行处理。另外由于它的实作与数据绑定,必须知道PDU所有的Classes,导致代码非常的难以维护::如果想处理一个新的PDU Class,那么除了必须要修改CFsRemoteView:: DataIndication,而且还要为此 PDU Class的处理添加一个新的成员函数。
毋庸置疑,改良这个函数的关键在于重构switch case部分语句。
仔细阅读一下代码,我们发现switch case语句各个case之前不同的主要有两点:
1
)创建一个PDU对象的语句。
2)
不同的处理PDU对象的成员函数。
针对这两点,我设计了一个新的处理机制,大致代码如下:
CDvPdu*CreateOfferPdu{}{ return new CFsFileOfferPdu; }
CDvPdu* CreateAcceptPdu {}{ return new CFsFileAcceptPdu; }
。。。

typedef CDvPdu* (*PNewPdu)(void);
typedef void (CFsRemoteView::*PCommander)();

typedef struct st_PduHandler
{
 int nPduType;
 int nCmdType;
 PNewPdu pNewPdu;
 PCommander pCommander;
}STPduHandler, *PSTPduHandler;

STPduHandler g_stPduHandlers[] =
{
 {FS_PDU_FILE_OFFER, NID_RV_FILE_OFFER, CreateOfferPdu, NULL},
{FS_PDU_FILE_ACCEPT,NID_RV_FILE_ACCEPT,CreateAcceptPdu,CFsRemoteView::CommanderFile}
。。。

}

int CFsRemoteView:: DataIndication(short priority, LPBYTE lpData, DWORD dwLength)

。。。
 
CDvMemArchive ar((LPBYTE)lpData, dwLength);
 WORD wType;
 ar >> wType;
for (int i = 0; i < NELEMS(g_stPduHandlers); i++)
 {
  if (g_stPduHandlers[i].nPduType == wType)
  {
   CDvPdu* pPdu = g_stPduHandlers[i].pNewPdu();
   pPdu->SerializeFrom(ar);
   if (g_stPduHandlers[i].pCommander)
   {
    (this->*(g_stPduHandlers[i].pCommander))();
   }
m_pCmdTarget->OnCommand(g_stPduHandlers[i].nCmdType,
(LPARAM)pPdu);
   delete pPdu;
  }
 }
 
。。。

 Return 0;
}
void CFsRemoteView::CommanderFile()
{
 m_nFlowControl = TRUE;
}
。。。
改动之后CFsRemoteView:: DataIndication仅仅只有40行左右,另外仅用2个成员函数就取代了以前近30个处理PDU Classes的成员函数.复杂度达到可以忽略不计。而且实现了实作与数据分离,添加删除任意的PDU Class都不需要修改此函数。
但是这个设计还是有稍稍不尽如人意的地方,那就是每个PDU Class都对应一个创建函数.几十个这样的函数堆在一起,看起来也很头疼.维护起来也还是没有达到“一行代码”的终极简便,怎么去简化这一步?
由于所有的PDU创建函数,都只是类型不用,所以很自然的想到采用模板来处理
.
template<Classes T>
CDvPdu* NewPdu()
{
 return New T;
}
STPduHandler g_stPduHandlers[] =
{
{FS_PDU_FILE_OFFER,ID_RV_FILE_OFFER,&NewPdu<CFsFileOfferPdu>, NULL},
 
。。。

}
 
可编译的时候,却遇上了错误,仔细检查原因才发现:
要想定义一个指向模板函数的函数指针,该指针也只能定义为一个基于模板的变量.

但模板只支持函数和类.所以我这里得使用struct为中间层,再次修改代码如下:
template<class T>
struct Creator
{
 static CDvPdu* NewPdu()
 {
  return new T;
 }
};
STPduHandler g_stPduHandlers[] =
{
 {FS_PDU_FILE_OFFER, NID_RV_FILE_OFFER,
Creator <CFsFileOfferPdu>::NewPdu, NULL},
{FS_PDU_FILE_ACCEPT,NID_RV_FILE_ACCEPT,
Creator <CFsFileAcceptPdu>::NewPdu, CFsRemoteView::CommanderFile},
 
。。。

}
重新编译测试,结果正确.

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值