分解大量switch-case分支的两种方法
项目经过长期多人的维护,所谓人多手杂,出现不少过多过长的switch-case分支,或者多重switch-case嵌套。每每添加功能,我都会紧皱眉头,然后带着罪恶感向已经成百上千行的函数里再添上一个case分支,然后纠结地收工了事。
于是乎,在我的内心深处,switch-case俨然成了代码坏味道的代名词,写代码时总小心翼翼地避开它们,可往往又事与愿违。
事实上,switch-case语句并不是代码坏味道的根源,坏味道来自糟糕的结构设计,过多的switch-case分支,多重switch-case嵌套,这些都将导致代码可读性下降,维护困难易出错。
对于分支有多又长的switch-case分支,可是使用表驱动方法或者在特定情况下可以采用表驱动方法结合事件机制进行分解。
对于表驱动方法,可用数组或std::map将case判定常量映射到相应的处理函数。核心代码实现类似如下:
// 分支判定枚举
enum CASE
{
_CASE_A,
...
_CASE_X,
_CASE_AMOUNT // 分支总数
};
// 处理函数类型
typedef void (*HANDLER)(void);
// 映射
HANDLE handlers[_CASE_AMOUNT] =
{
&HandlerA,
...
&HandlerX
};
bool handler(UINT uEvent)
{
if (uEvent < _CASE_AMOUNT)
{
(*handlers[uEvent])();
return true;
}
return false;
}
优点:条理清晰易于阅读。
缺点:要增添很多处理函数,且不便于跟踪调试。
对于在一个公共对象里使用switch-case分支集中处理大量与该对象无紧密逻辑关联事件的情况。可通过在公共基类中使用事件处理机制,实现事件与监听器(与事件相关的对象,如窗口)的映射,事件的分配处理。相关代码如下:
class CBase
{
...
public:
// 添加事件监听器
static bool AddEventListener(UINT uEvent, CBase* pListener);
// 移除事件监听器
static bool RemoveEventListener(UINT uEvent, CBase* pListener);
// 将事件给相应监听器处理
static void DispatchEvent(UINT uEvent);
// 处理事件,该虚函数由监听器实现
virtual void OnEvent(UINT uEvent, int nParam) {}
...
private:
// 事件与监听器的映射,多个监听器可监听同一个事件
static std::multimap<UINT, CBase*> m_mmapEventListener;
...
}
对于事件处理机制,在Java等语言中有大量使用,这里就不贴一大段代码了。
优点:逻辑性强,耦合度低,符合开放封闭原则。
缺点:事件分配时效率较switch-case分支低。
分解大量switch-case分支的两种方法的更多相关文章
- CircleCi 不更新某个分支的两种方法
概述 今天我发现我的所有项目的 CircleCi 部署全部都会更新 gh-pages 分支.找了好久,终于找到了不更新的方法.于是我总结了一下,记录下来,供以后开发时参考,相信对其他人也有用. onl ...
- 【2017-2-23】C#switch case分支语句,for循环语句
switch case分支语句 switch(一个变量值) { case 值:要执行的代码段;break; case 值:要执行的代码段;break; … default:代码段;break;(def ...
- CASE WHEN的两种格式
CASE WHEN的两种格式 1.简单Case函数 CASE sex WHEN '1' THEN '男' WHEN '2' THEN '女' ELSE '其他' END 2.Case搜索函数 CASE ...
- 在sql中case子句的两种形式
case子句,在select后面可以进行逻辑判断. 两种形式:判断相等.判断不等 一.判断相等的语法: case 列名 when ... then ... when ... then ... el ...
- windows下获取IP地址的两种方法
windows下获取IP地址的两种方法: 一种可以获取IPv4和IPv6,但是需要WSAStartup: 一种只能取到IPv4,但是不需要WSAStartup: 如下: 方法一:(可以获取IPv4和I ...
- android 之 启动画面的两种方法
现在,当我们打开任意的一个app时,其中的大部分都会显示一个启动界面,展示本公司的logo和当前的版本,有的则直接把广告放到了上面.启动画面的可以分为两种设置方式:一种是两个Activity实现,和一 ...
- Linux 下操作GPIO(两种方法,驱动和mmap)(转载)
目前我所知道的在Linux下操作GPIO有两种方法: 1.编写驱动,这当然要熟悉Linux下驱动的编写方法和技巧,在驱动里可以使用ioremap函数获得GPIO物理基地址指针,然后使用这个指针根据io ...
- Linux 下操作gpio(两种方法,驱动和mmap)
目前我所知道的在linux下操作GPIO有两种方法: 1. 编写驱动,这当然要熟悉linux下驱动的编写方法和技巧,在驱动里可以使用ioremap函数获得GPIO物理基地址指针,然后使用这个指针根据 ...
- QT中获取选中的radioButton的两种方法(动态取得控件的objectName之后,对名字进行比较)
QT中获取选中的radioButton的两种方法 QT中要获取radioButton组中被选中的那个按钮,可以采用两种如下两种办法进行: 方法一:采用对象名称进行获取 代码: 1 QRadioBu ...
随机推荐
- 【KTU Programming Camp (Day 3)】Queries
http://codeforces.com/gym/100739/problem/A 按位考虑,每一位建一个线段树. 求出前缀xor和,对前缀xor和建线段树. 线段树上维护区间内的0的个数和1的个数 ...
- [LOJ2542][PKUWC2018]随机游走(MinMax容斥+树形DP)
MinMax容斥将问题转化为求x到S中任意点的最小时间. 树形DP,直接求概率比较困难,考虑只求系数.最后由于x节点作为树根无父亲,所以求出的第二个系数就是答案. https://blog.csdn. ...
- [BZOJ4570][SCOI2016]妖怪(凸包)
两种做法,前一种会TLE. 第一种是高一数学题做法,设一个妖怪的atk和dnf分别为x和y,则它在(a,b)环境下的战斗力为x+y/a*b+y+x/a*b. 设t为b/a,则战斗力即$f(x,y,t) ...
- ZJOI2018 day2游记
省选讲课:还不错吧 ZJOI RP++ Day2: 题出的好!覆盖知识点广,题目又着切合实际的背景,解法比较自然. 给出题人点赞 ! 意识模糊地点开了题(考前不熬夜似乎还是很困qaq) T1:前一个小 ...
- iptables配置允许mysql远程访问
vi /etc/sysconfig/iptables iptables -A INPUT -p tcp -m state --state NEW -m tcp --dport 3306 -j ACCE ...
- 【BZOJ】4985: 评分【DP】
4985: 评分 Time Limit: 10 Sec Memory Limit: 64 MBSubmit: 148 Solved: 75[Submit][Status][Discuss] Des ...
- POJ 1321 棋盘问题 DFS 期末前水一水就好……
A - 棋盘问题 Time Limit:1000MS Memory Limit:10000KB 64bit IO Format:%I64d & %I64u Submit Sta ...
- Idea详细配置
https://blog.csdn.net/m_m254282520/article/details/78900238
- ROS知识(20)----使用Master_API查询Master管理的节点话题服务内容
在一些应用中会需要获取master的uri地址,发布的话题,订阅的话题,发布的服务,节点的信息等等.这些功能我们通常可一通过rosnode list, rosnode info, rostopic l ...
- GIT(5)----常用命令:新分支,切换,删除,远程推送,标签,日志
常用命令 1.新分支 创建新分支:git branch <branch_name> 创建分支并切换到新的分支上:git checkout -b <branch_name> 2. ...