项目经过长期多人的维护,所谓人多手杂,出现不少过多过长的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分支的两种方法的更多相关文章

  1. CircleCi 不更新某个分支的两种方法

    概述 今天我发现我的所有项目的 CircleCi 部署全部都会更新 gh-pages 分支.找了好久,终于找到了不更新的方法.于是我总结了一下,记录下来,供以后开发时参考,相信对其他人也有用. onl ...

  2. 【2017-2-23】C#switch case分支语句,for循环语句

    switch case分支语句 switch(一个变量值) { case 值:要执行的代码段;break; case 值:要执行的代码段;break; … default:代码段;break;(def ...

  3. CASE WHEN的两种格式

    CASE WHEN的两种格式 1.简单Case函数 CASE sex WHEN '1' THEN '男' WHEN '2' THEN '女' ELSE '其他' END 2.Case搜索函数 CASE ...

  4. 在sql中case子句的两种形式

    case子句,在select后面可以进行逻辑判断. 两种形式:判断相等.判断不等 一.判断相等的语法: case 列名 when ...  then ... when ...  then ... el ...

  5. windows下获取IP地址的两种方法

    windows下获取IP地址的两种方法: 一种可以获取IPv4和IPv6,但是需要WSAStartup: 一种只能取到IPv4,但是不需要WSAStartup: 如下: 方法一:(可以获取IPv4和I ...

  6. android 之 启动画面的两种方法

    现在,当我们打开任意的一个app时,其中的大部分都会显示一个启动界面,展示本公司的logo和当前的版本,有的则直接把广告放到了上面.启动画面的可以分为两种设置方式:一种是两个Activity实现,和一 ...

  7. Linux 下操作GPIO(两种方法,驱动和mmap)(转载)

    目前我所知道的在Linux下操作GPIO有两种方法: 1.编写驱动,这当然要熟悉Linux下驱动的编写方法和技巧,在驱动里可以使用ioremap函数获得GPIO物理基地址指针,然后使用这个指针根据io ...

  8. Linux 下操作gpio(两种方法,驱动和mmap)

    目前我所知道的在linux下操作GPIO有两种方法: 1.  编写驱动,这当然要熟悉linux下驱动的编写方法和技巧,在驱动里可以使用ioremap函数获得GPIO物理基地址指针,然后使用这个指针根据 ...

  9. QT中获取选中的radioButton的两种方法(动态取得控件的objectName之后,对名字进行比较)

    QT中获取选中的radioButton的两种方法   QT中要获取radioButton组中被选中的那个按钮,可以采用两种如下两种办法进行: 方法一:采用对象名称进行获取 代码: 1 QRadioBu ...

随机推荐

  1. JQuery的源码阅读

    探索原理,animation实现,一个对象可以同时绑定多个事件,这是如何实现的? (function(window, undefined) { function jQuery(selector){ r ...

  2. 使用Java创建Excel,并添加内容

    使用Java创建Excel,并添加内容 一.依赖的Jar包 jxl.jar,使用jxl操作Excel Jxl是一个开源的Java Excel API项目,通过Jxl,Java可以很方便的操作微软的Ex ...

  3. SDOI 2017 Round2 滚粗了

    没进省队qwq 技不如人,甘拜下风

  4. vijos 1894 二分

    题意:在 Ninian 的花园里,有许多琼花,环绕着中间的凉亭.有 N 片琼花,组成一个环.Ninian 想在凉亭中发动 [セチの祈り] , 需要划分出三个区域的琼花,为了平均,要最大化面积最小的区域 ...

  5. Xcode 小技巧

    1.手动添加 #warning ,在不确定的 bug.错误.待定代码处,手动添加 #warning 行,在编译时间提醒自己需要处理的地方. 2.由于 arrayWithObjects: 和 initW ...

  6. Python知识(1)----基础入门和进阶总结。

    今天把Python的语法过了一遍,学习了慕课网上的教程,简单易懂,1个小时就可以入门Python了.Python有两个主要的版本,Python2.7,Python3.5,后面的版本,改动较大,编写的程 ...

  7. 文件上传demo

    前端代码: <form action="upload.php" enctype="multipart/form-data" method="po ...

  8. interfacer和abstarct class的异同

  9. vim segment fault when i upgrade to macOS Mojave 103_PollServerReady

    系统升级到 macOS Mojave, vim插件YouCompleteMe出错. Vim: Caught deadly signal SEGV Error detected while proces ...

  10. Android证书有效性验证方案

    1.前言: 1.1.SSL劫持攻击:          目前虽然很多Android APP使用了https通信方式,但是只是简单的调用而已,并未对SSL证书有效性做验证.在攻击者看来,这种漏洞让htt ...