表驱动编程实践 | 简化代码中复杂的if-else逻辑


最近做的几个项目都涉及到了比较复杂的if-else逻辑,刚开始因为是自己码的,所以觉得还行,赶脚自己能hold住,但后面因为在多个项目上不停切换,导致撸主的大脑在切换上下文时,总要重新回忆下复杂的代码逻辑,搞得撸主欲生欲死。
于是今天的主角出场了,撸主死磕了下《代码大全》的表驱动章节后,把之前复杂的代码逻辑,用表驱动法重新调整了下,代码的可读性立马就飙升了几个Level!! Oh yeah妈妈再也不用担心上下文切换了~

废话不多说,上代码。

Lua实现
先来看看原来的肮脏代码:

function do_plug_set_opt(res, plug_opt, plug_code, plug, plug_param)
    local format_res = res
    local code = nil

    if plug_code and plug_opt == "install" then
        local install_node  = check_plug_code_flag(plug_code)
        local plug_install_location, specify_res   = specify_install_storage_node(install_node, res)
        if specify_res["code"] then
            require "MZLog".log(3, res)
            log_res(plug_param.plug_opt, res)
            return specify_res
        end
        res, code = install_plugin(plug_code, plug_param.plug_md5, plug_param.plug_url, plug_install_location)

    elseif plug_code and plug["plug_name"] then
        local plug_exec_fname = get_plug_exec_file_name(plug["plug_code"])
        init_plug_install_local_path(plug_location)
        if plug_opt == "remove" then
            res = remove_plugin(plug_code, plug_exec_fname)

        elseif plug_opt == "start"then
            res = start_plugin(plug_code, plug_exec_fname)

        elseif plug_opt == "stop" then
            res = stop_plugin(plug_code, plug_exec_fname)

        elseif plug_opt == "cmd_ctrl" then
            res = control_plugin(plug_param.cmd_argc_list, plug_exec_fname)

        elseif plug_opt == "upgrade" then
            res, code = upgrade_plugin(plug_code, plug_param.plug_md5, plug_param.plug_url)
        end
    end
    if code == nil then
        format_res = print_res(format_res, res, PLUG_OPT_CODE.NORMAL)
    else
        format_res = print_res(format_res, res, code)
    end
    return format_res
end

是不是觉得被各种if-else绕晕了?
是不是一看到这一大坨代码,瞬间赶脚不会再爱了?
别急,再看看优化之后的代码:

plug_set_opt_action         = {
    ["install"]             = plug_opt_util.install_plugin,
    ["async_install"]       = plug_opt_util.async_install_plugin,
    ["remove"]              = plug_opt_util.remove_plugin,
    ["async_remove"]        = plug_opt_util.async_remove_plugin,
    ["start"]               = plug_opt_util.start_plugin,
    ["stop"]                = plug_opt_util.stop_plugin,
    ["cmd_ctrl"]            = plug_opt_util.control_plugin,
    ["upgrade"]             = plug_opt_util.upgrade_plugin,
    ["async_upgrade"]       = plug_opt_util.async_upgrade_plugin
}

function do_plug_set_opt(res, plug_opt, plug_code, plug, plug_param)
    local format_res, code = res, nil
    if plug["plug_name"] then
        plug_param.plug_exec_fname = plug_info_util.get_plug_exec_file_name(plug["plug_code"])
        local plug_location = plug_info_util.get_plug_location()
        init_plug_install_local_path(plug_location)
        code, res = plug_set_opt_action[plug_opt](plug_param)
    end
    return print_res(format_res, res, code)
end

不得不说,代码可读性高太多了,有木有~~
lua非主流?好吧,辣偶用Java实现个...

Java实现
肮脏的代码就不贴了,直接上优化以后的代码哈~

public static Map<Mode_Action, String> mode_map;
private static void init_mode_map() {
    mode_map = new HashMap<Mode_Action, String>();
    mode_map.put(Mode_Action.HOME_IN_MODE,    "回家模式");
    mode_map.put(Mode_Action.HOME_OUT_MODE,   "离家模式");
    mode_map.put(Mode_Action.MOVIE_MODE,      "清晨模式");
    mode_map.put(Mode_Action.MOVIE_MODE,      "沐浴模式");
    mode_map.put(Mode_Action.MOVIE_MODE,      "观影模式");
}

static MZGrammar identfy_mode_device(String speech) {
    Mode_Action mode_action = null;
    init_mode_map();
    for (Map.Entry<Mode_Action, String> entry: mode_map.entrySet())
    {
        if (speech.contains( entry.getValue() )) {
            mode_action = entry.getKey();
            break;
        }
    }
    MZGrammar rtn_grammar = null;
    if ( mode_action == null ) {
        rtn_grammar = new MZGrammar(mode_action);
    } 
    return rtn_grammar;
}

下面这一份代码,赶脚还有优化空间,但又不知从何下手,还请各位帮忙Review,提点意见哈~

public static Map<IR_Action, ArrayList<String> > ir_map;

private static void add_key_word(IR_Action ir_action, String ...args) {
    ArrayList<String> ir_str_ar = new ArrayList<String>();
    for (int i = 0; i < args.length; ++i) {
        ir_str_ar.add(args[i]);
    }
    ir_map.put(ir_action, ir_str_ar);
}

private static void init_ir_map() {
    ir_map = new HashMap<IR_Action, ArrayList<String>>();

    add_key_word(IR_Action.TV_UP_VOL,         "加", "音量");
    add_key_word(IR_Action.TV_DOWN_VOL,       "减", "音量");
    add_key_word(IR_Action.TV_UP_CHANNEL,     "加", "频道");
    add_key_word(IR_Action.TV_DOWN_CHANNEL,   "减", "频道");
    add_key_word(IR_Action.TV_SWITCH_OFF,     "关", "电视");
    add_key_word(IR_Action.TV_SWITCH_ON,      "开", "电视");

    add_key_word(IR_Action.AIR_SWITCH_OFF,    "关", "空调");
    add_key_word(IR_Action.AIR_SWITCH_ON,     "开", "空调");

    add_key_word(IR_Action.AIR_TEMPERATURE_UP,    "升温", "空调");
    add_key_word(IR_Action.AIR_TEMPERATURE_DOWN,  "降温", "空调");
}

static MZGrammar identfy_ir_device(String speech) {
    IR_Action ir_action = null;
    init_ir_map();
    
    for (Map.Entry<IR_Action, ArrayList<String> > entry: ir_map.entrySet()) {
        ArrayList<String> keywords = entry.getValue();
        boolean is_keyword_contains = true;
        for ( String keyword : keywords ) {
            if ( !speech.contains(keyword) ) {
                is_keyword_contains = false;
                break;
            }
        }
        if (is_keyword_contains) {
            ir_action = entry.getKey();
            break;
        }
    }
    int type = HomeDevice.FLAG_TYPE_IRC;
    return new MZGrammar(ir_action, type);
}

C实现
神马?C没有基础的键值对结构,肿么实现?
没事,没有结构,创造结构也要上

/*
 * UI_EVENT
 */
typedef enum
{
    UI_SWITCH_UI_MODE       = 0,
    UI_DEMO_FUNC            = 1,
    UI_RECV_LPRESS_KEY      = 2,
    UI_RECV_SPRESS_KEY      = 3,
    UI_RECV_KEY_UP          = 4,
    UI_RECV_KEY_DOWN        = 5,
    UI_GET_SWITCH_UI_MODE   = 6,
    UI_GET_BLE_STATE        = 7,
    UI_GET_SYS_STATE        = 8,
    UI_CLOSE_BLE            = 9,
    UI_OPEN_BLE             = 10,
    UI_ALARM_REMIND         = 11,
    UI_SEDENTARY_REMIND     = 12,
    UI_RTC_SYNC_STEPS       = 13,
    UI_LOW_BATTERY_WARNING  = 14,
} ui_task_id_t;

static const MODULE_EVENT ui_event_table[] =
{
    {UI_SWITCH_UI_MODE,         switch_ui_mode},
    {UI_DEMO_FUNC,              demo_func},
    {UI_RECV_LPRESS_KEY,        recv_lpress_key},
    {UI_RECV_SPRESS_KEY,        recv_spress_key},
    {UI_RECV_KEY_UP,            recv_key_up},
    {UI_RECV_KEY_DOWN,          recv_key_down},
    {UI_GET_SWITCH_UI_MODE,     get_ui_mode},
    {UI_GET_BLE_STATE,          get_ble_state},
    {UI_GET_SYS_STATE,          get_sys_state},
    {UI_CLOSE_BLE,              ui_close_ble},
    {UI_OPEN_BLE,               ui_open_ble},
    {UI_ALARM_REMIND,           ui_alarm_remind},
    {UI_SEDENTARY_REMIND,       ui_sedentary_remind},
    {UI_RTC_SYNC_STEPS,         ui_rtc_sync_steps},
    {UI_LOW_BATTERY_WARNING,    ui_low_battery_warning},
};

static const int UI_EVENT_SIZE = sizeof(ui_event_table) / sizeof(MODULE_EVENT);


/*
 * EventType
 */
typedef struct __EventType
{
    uint8_t task_id;
    const MODULE_EVENT *module_event_table;
} EventType;

static const EventType event_type_table[] =
{
    {TASK_ID_WATCH_HANDLER,     watch_event_table},
    {TASK_ID_BT,                bt_event_table},
    {TASK_ID_SENSOR,            sensor_event_table},
    {TASK_ID_ADC,               adc_event_table},
    {TASK_ID_KEY,               key_event_table},
    {TASK_ID_UI,                ui_event_table},
};

void snd_msg2ui_task(task_message *msg)
{
    if (pdPASS != xQueueSendToBack(ui_msg_queue, (void *) &msg, 0)) {
        vPortFree(msg);
    }
}
/*
 * MsgType
 */
typedef struct __MsgType
{
    uint8_t task_id;
    void (*eventFunc)(task_message *);
} MsgType;
static const MsgType msg_type_table[] =
{
    {TASK_ID_WATCH_HANDLER,     snd_msg2watch_task},
    {TASK_ID_BT,                snd_msg2bt_task},
    {TASK_ID_SENSOR,            NULL},
    {TASK_ID_ADC,               NULL},
    {TASK_ID_KEY,               NULL},
    {TASK_ID_UI,                snd_msg2ui_task},
};
static const uint8_t TB_MSG_TYPE_SIZE = sizeof(msg_type_table) / sizeof(MsgType);

void dispatch_msg(task_message *msg)
{
    void (*dispatch_msg_func)(task_message *);
    for(int i = 0; i < TB_MSG_TYPE_SIZE; ++i) {
        if (event_type_table[i].task_id == msg->task_id_dst) {
            dispatch_msg_func = msg_type_table[msg->task_id_dst].eventFunc;
            break;
        }
    }
    dispatch_msg_func(msg);
}

void unit_test_switch_ui_mode(void)
{
    task_message *snd_msg = construct_task_msg(false, TASK_ID_UI, TASK_ID_SENSOR, UI_SWITCH_UI_MODE);
    dispatch_msg(snd_msg);
}

当你手上有一把锤子的时候,看所有的东西都是钉子;
当你手上有一个钉子的时候,看所有的东西都是锤子;
当你刚掌握了表驱动的时候,看所有的代码都想把他改成表驱动形式。
不得不说,撸主现在就是这种状态,大家别笑话哈~~

OK,厕所文写完啦,更多详情,请各位去拜读《代码大全》第十八章哈~~
注:重点关注查表的三种方式:直接访问,索引访问,阶梯访问~~

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270

推荐阅读更多精彩内容