JefferyZhou

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

 

    前面一段时间在宠物模块重构的时候,惊叹这个模块的好几个函数长度居然有达到3000行,
在重构这块的时候用,利用函数指针写了一个小的工具模板。函数指针与成员函数指针,
在一些进阶应用中时有涉及,比如常见的EventMap,
这里介绍一个经常用成员函数指针来对长长switch的进行的重构手法。
    先简单的说明一下函数指针与成员函数指针:

    前面一段时间在宠物模块重构的时候,惊叹这个模块的好几个函数长度居然有达到3000行,在重构这块的时候用,利用函数指针写了一个小的工具模板。函数指针与成员函数指针,在一些进阶应用中时有涉及,比如常见的EventMap,这里介绍一个经常用成员函数指针来对长长switch的进行的重构手法。
    先简单的说明一下函数指针与成员函数指针:

 

 

#include<iostream>
	
	class CPlusPlus_Class;
	
        //声明了一个函数指针类型 pfnHandle_C, 这个函数返回void,参数也是void
	typedef void (*pfnHandle_C)(void);						
	
        //声明了一个成员函数指针类型 pfnHandle_CPP,这个函数是 CPlusPlus_Class 类的成员函数,返回void,参数也是void
	typedef void (CPlusPlus_Class::*pfnHandle_CPP)(void);   
	
	
	void egFunc_C(void)
	{
		//output egFunc_C
		std::cout<<"egFunc_C"<<std::endl;
	}
	
	class CPlusPlus_Class
	{
	public:
		void egFunc_CPP(void)
		{
			//output egFunc_CPP
			std::cout<<"CPlusPlus_Class::egFunc_CPP"<<std::endl;
		};
	}
	
	int _tmain(int argc, _TCHAR* argv[])
	{
		pfnHandle_C pfn_C_VV = egFunc_C;
		
		egFunc_C()
		pfn_C_VV();
		
		CPlusPlus_Class objCpp;
		
		pfnHandle_CPP pfn_CPP_VV = &CPlusPlus_Class::egFunc_CPP;
		
		objCpp.egFunc_CPP();
		(objCpp.*pfn_CPP_VV)();
	}

 

在一个初始的设计当中,往往容易出现如下的函数结构:

 

int CPlusPlus_Class::eventHandle(int eventChoice, int lfsParam, int rfsParam)
{
    int nRet = E_SYS_SUCCESS;

    // Do some thing
    //.............
    swithc(eventChoice)
   {
    case  E_EVENT_CHOICE_A:   // 1
           {
           }
           break;

    case  E_EVENT_CHOICE_B:   // 2
           {
           }
           break;

    case  E_EVENT_CHOICE_C:   // 3
           {
           }
           break;

    case  E_EVENT_CHOICE_D:   // 4
           {
           }
           break;

     case  E_EVENT_CHOICE_E:   // 5
           {
           }
           break;
   }

   return nRet;
}

 在开始的时候,这个结构还是相当的美观的,随着开发的深入,这个函数增长超出你的想象,而且结构越来越混乱,哪些该放在switch里面 ,哪些改放在 switch前面,哪些该放在switch后面,也让你越来越纠结。在工作当中,我接手的一个模块就很多地方有这种冗长的接口,记得一个处理宠物使用道具的接口,总共有15个switch分支(总共有15种道具可对宠物使用),接口函数的总长度达到3568行,全部是逻辑代码,这个函数从前面看到后面最终也不知道自己再看什么了。为了分解这个接口,用了一个函数指针表来存储入口,然后根据选项计算接口地址。最终用模板来表达这个概念,在同一个类里面把算法封装到各个子接口,然后把算法子接口存储在一张表里面,根据选择项调用不同的算法接口。

template <class HandleClass, class EventReceiver>
class IClassFunctionMap
{
public:
	typedef int (HandleClass::*pfnEventHandle)(EventReceiver* pReceiver, int nParam1, int nParam2, int nParam3, int nParam4, const char* pszChar);
	typedef std::map<int, typename IClassFunctionMap::pfnEventHandle > EventHandleMap;
	typedef typename EventHandleMap::iterator EventHandleMapIter;
	typedef typename EventHandleMap::const_iterator EventHandleMapConstIter;

	virtual int AddEventHandle(int nChoice, pfnEventHandle pfnHandle)
	{
		m_HandleMap[nChoice] = pfnHandle;
		return COMMONUTIL_SUCCE;
	}

	virtual int DelEventHandle(int nChoice)
	{
		pfnEventHandle pfnHandle = FindEventHandle(nChoice);
		if (pfnHandle)
		{
			m_HandleMap.erase(nChoice);
		}
		return COMMONUTIL_SUCCE;
	}


	virtual int DoEventHandle(int nChoice, EventReceiver* pReceiver, int nParam1, int nParam2, int nParam3, int nParam4, const char* pszChar)
	{
		const pfnEventHandle pfnHandle = FindEventHandle(nChoice);
		if (pfnHandle)
		{
			HandleClass* pHandClass = static_cast<HandleClass*>(this);
			if (pHandClass)
			{
				(pHandClass->*pfnHandle)(pReceiver, nParam1, nParam2, nParam3, nParam4, pszChar);
				return COMMONUTIL_SUCCE;
			}
			
		}
		return COMMONUTIL_ERROR;

	}
protected:

	virtual pfnEventHandle FindEventHandle(int nChoice)const
	{
		EventHandleMapConstIter ite = m_HandleMap.find(nChoice);
		if (ite != m_HandleMap.end())
		{
			return ite->second;
		}
		return NULL;
	}
private:
	EventHandleMap m_HandleMap;
};

class ExEventHandleFromInt : public IClassFunctionMap<ExEventHandleFromInt, int>
{
public:
	int ExEventHandle_1(int* pRe, int Param1, int Param2, int Param3, int Param4, const char* pszChar)
	{
		std::cout<<"ExEventHandle_1 : "<<Param1<<std::endl;
		return 1;
	}
	int ExEventHandle_2(int* pRe, int Param1, int Param2, int Param3, int Param4, const char* pszChar)
	{
		std::cout<<"ExEventHandle_2 : "<<Param1<<std::endl;
		return 1;
	}
	int ExEventHandle_3(int* pRe, int Param1, int Param2, int Param3, int Param4, const char* pszChar)
	{
		std::cout<<"ExEventHandle_3 : "<<Param1<<std::endl;
		return 1;
	}
	int ExEventHandle_4(int* pRe, int Param1, int Param2, int Param3, int Param4, const char* pszChar)
	{
		std::cout<<"ExEventHandle_4 : "<<Param1<<std::endl;
		return 1;
	}
protected:
private:
};

#define  OUTPUT_SEPARATOR " "
int _tmain(int argc, _TCHAR* argv[])
{
	ExEventHandleFromInt stExHandle;

	stExHandle.AddEventHandle(1,  &ExEventHandleFromInt::ExEventHandle_1);
	stExHandle.AddEventHandle(2,  &ExEventHandleFromInt::ExEventHandle_2);
	stExHandle.AddEventHandle(3,  &ExEventHandleFromInt::ExEventHandle_3);
	stExHandle.AddEventHandle(4,  &ExEventHandleFromInt::ExEventHandle_4);

	stExHandle.DoEventHandle(1, NULL, 1, 2, 3, 4, NULL);
	stExHandle.DoEventHandle(2, NULL, 1, 2, 3, 4, NULL);
	stExHandle.DoEventHandle(3, NULL, 1, 2, 3, 4, NULL);
	stExHandle.DoEventHandle(4, NULL, 1, 2, 3, 4, NULL);
	stExHandle.DoEventHandle(5, NULL, 1, 2, 3, 4, NULL);

	stExHandle.DelEventHandle(1);

	stExHandle.DelEventHandle(3);

	stExHandle.DoEventHandle(1, NULL, 1, 2, 3, 4, NULL);
	stExHandle.DoEventHandle(2, NULL, 1, 2, 3, 4, NULL);
	stExHandle.DoEventHandle(3, NULL, 1, 2, 3, 4, NULL);
	stExHandle.DoEventHandle(4, NULL, 1, 2, 3, 4, NULL);

	stExHandle.AddEventHandle(1,  &ExEventHandleFromInt::ExEventHandle_4);

	stExHandle.AddEventHandle(3,  &ExEventHandleFromInt::ExEventHandle_4);

	stExHandle.DoEventHandle(1, NULL, 1, 2, 3, 4, NULL);
	stExHandle.DoEventHandle(2, NULL, 1, 2, 3, 4, NULL);
	stExHandle.DoEventHandle(3, NULL, 1, 2, 3, 4, NULL);
	stExHandle.DoEventHandle(4, NULL, 1, 2, 3, 4, NULL);
	
	system("pause");
	return 0;
}

上面代码中可以非常清晰的看到用一个IClassFunctionMap::DoEventHandle接口处理策略分配。而具体的处理工作都分布在ExEventHandle_1 , ExEventHandle_2...中,而且这个策略是一个动态变化的,

可以在需要的时候通过接口IClassFunctionMap::AddEventHandle和接口IClassFunctionMap::DelEventHandle改写策略map,


后记,上面的实现可以考虑用一个固定大小的数组来替代map(C++虚函数表就是一个127的数组),优化执行效率。

Sign,  Clown , 2010.06.30 . 19:45 . HDPY

[本文原创,转载请注明出处,在文章末尾提供原文链接http://www.cnblogs.com/JefferyZhou/,否则一旦发现,将按字节每人民币收费,绝不论价]

posted on 2010-06-30 19:11  JefferyZhou  阅读(1505)  评论(2编辑  收藏  举报