IT技术博客大学习 共学习 共进步

如何有效避免大量重复的switch分支

忘我的追寻 2013-10-29 12:21:06 浏览 2,725 次

   最近学习设计模式相关知识,结合前面的DRBD源码分析掌握了表驱动编程模式,这里作一些简单的总结。

   先看一段C代码:

typedef int type;    typedef void(*draw)(void);     struct shape    {        type t;        draw f;    };    struct rectange    {        type t;        int a;        draw f;    };    struct circle    {        type t;        int r;        draw f;    };    #define T1 0    #define T2 1    #define T3 2    void drawall(shape[] s, int count)    {        for (int i = 0; i != count; i++)        {            switch((s + i)->t)            {            case T1:                ((struct shape*)(s + i))->f();                break;            case T2:                ((struct rectange*)(s + i))->f();                break;            case T3:                ((struct circle*)(s + i))->f();                break;            default:                break;            }        }    }
代码中需要根据图形的形状去调用具体的draw方法,对type的判断只是为了确定该调用哪个结构体中的draw类型的函数。那么能否简化一下这个switch case呢?最简单的,修改各个抽象形状的结构体定义,然后定义一个共有的“基类”,即只定义类型和函数指针,将各种形状对象强转为“基类型”,然后统一调用函数指针即可,同时可以将指向形状对象的基类指针作为参数传入,在函数中再将基类型的指针转为具体的子类兴。达到去除case switch的目的。

   按照如上思路修改之后的代码应该是类似这样的:

struct base    {        type t;        draw f;    };    typedef int type;    typedef void(*draw)(struct base*);     struct shape    {        type t;        draw f;    };    struct rectange    {        type t;        draw f;        int a;    };    struct circle    {        type t;        draw f;        int r;    };    #define T1 0    #define T2 1    #define T3 2    void drawall(struct base[] s, int count)    {        struct base* b = s;        for (int i = 0; i != count; i++)        {            (b + i)->draw(b + i);        }    }
这样,要求所有的类型都应该“符合”base类型的结构,当出现不符合该类型的结构传入时,编译时并不会报错,运行时才会寻址错误。这样做不是特别好。

   按照表驱动模式进一步改造该代码:

struct config    {        type t;        int l;    };    typedef int type;    typedef void(*draw)(struct config*);     void drawshape(struct config*);    void drawsrectange(struct config*);    void drawcircle(struct config*);    #define T1 0    #define T2 1    #define T3 2    draw call_table[] = {        [T1] = {&drawshape},        [T2] = {&drawsrectange},        [T3] = {&drawcircle},    };    void drawall(struct config[] s, int count)    {        draw* d = call_table;        struct config* b = s;        for (int i = 0; i != count; i++)        {            (*(d + (b + i)->t))(b + i);        }    }

   这样代码看起来是简洁了,但是可读性降低了不少。

建议继续学习

  1. 从Java视角理解CPU上下文切换(Context Switch) (阅读 6,447)
  2. 从 if else 到 switch case 再到抽象 (阅读 3,303)
  3. JavaScript:假如default不是switch的最后一项 (阅读 2,887)
  4. Switch Case中的经典 (阅读 2,685)