論如何去掉那些噁心的switch-case和if--else if的代碼

前言

     在這裏需要提前聲明的是,並不是swicth--caseif---else if等代碼結構不好。在一般情況下(純屬個人能接受的範圍),在1~5個的情況下,使用上述的結構,還是可以接受的,而且代碼維護起來,也不用那麼費勁。但是如果超過5個以上的分支,個人就不是很建議還是使用這種結構來處理代碼了,因爲代碼不僅難看,而且這麼一大坨代碼,維護起來真是心裏MMP。

    那麼我們該如何來重構這部分的代碼?

    先來個示例代碼,就是需要多種分支那種,就好比要做一個計算器,輸入兩個數的值,然後進行加、減、乘、除、平方、立方、開方等等的運算操作。

   先來個噁心的代碼示例:


#include <stdio.h>

//使用switch-case結構,自己腦補下
double result_get_version1(double num1,double num2,char cal_type)
{
    double ret = 0.0;
    if(cal_type == '+')
    {
        ret = num1 + num2;
    }
    else if(cal_type == '-')
    {
        ret = num1 - num2;
    }
    else if(cal_type == '*')
    {
        ret = num1 * num2;
    }
    else if(cal_type == '/')
    {
        ret = num1 / num2;//這裏先不判斷除零異常
    }
    else if(cal_type == '2')//這裏暫且用2來代表平方運算
    {
        ret = num1 * num1;//簡化,num2不起作用
    }
    else if(cal_type == '3')//這裏暫且用3代表立方
    {
        ret = num1 * num1 * num1;//簡化,num2不起作用
    }
    else
    {
        printf("error cal_type!\r\n");//暫且支持這麼多類型的計算,
    }

    return ret;
}

int main(int argc,char *argv[])
{
    double val = 0.0;

    val = result_get_version1(10,20,'+');
    printf("value = %f\r\n",val);
    val = result_get_version1(10,20,'-');
    printf("value = %f\r\n",val);
    val = result_get_version1(10,20,'*');
    printf("value = %f\r\n",val);
    val = result_get_version1(10,20,'/');
    printf("value = %f\r\n",val);
    val = result_get_version1(10,20,'2');
    printf("value = %f\r\n",val);
    val = result_get_version1(10,20,'3');
    printf("value = %f\r\n",val);
}

運行結果如下:

如果,我這裏是說如果後面還要支持100種運算的話,那我們是不是要再寫100個else if的判斷語句?如果你真的是照這樣的寫法寫下去,那麼恭喜你了,你中大獎了。有人就反駁了,本來就是要判斷這麼多條件的,不寫這麼多分支怎麼判斷的完?事實上真的是這樣嗎?實際上我們可以有更好的辦法來解決這種分支多的代碼。

現在就讓我們來重構下代碼吧:

#include <stdio.h>


typedef struct CAL_FUNC_MAPPING
{
    char cal_type;
    double (*result_get)(double num1,double num2);
}cal_func_mapping;


double result_get_add(double num1,double num2)
{
    return num1 + num2;
}

double result_get_sub(double num1,double num2)
{
    return num1 - num2;
}

double result_get_mul(double num1,double num2)
{
    return num1 * num2;
}

double result_get_div(double num1,double num2)
{
    return num1 / num2;
}
//暫且表示平方
double result_get_2(double num1,double num2)
{
    return num1 * num1;
}
//暫且表示立方
double result_get_3(double num1,double num2)
{
    return num1 * num1 * num1;
}
//添加其他運算,繼續往後添加
//....


static cal_func_mapping cal_func_mapping_table[] =
{
    {
        '+',
        result_get_add
    },
    {
        '-',
        result_get_sub
    },
    {
        '*',
        result_get_mul
    },
    {
        '/',
        result_get_div
    },
    {
        '2',
        result_get_2
    },
    {
        '3',
        result_get_3
    }
    //增加其他運算,繼續往後添加
    // ...
};

double result_get_version2(double num1,double num2,char cal_type)
{
    int count = sizeof(cal_func_mapping_table) / sizeof(cal_func_mapping);
    int i = 0;
    double ret = 0.0;

    for(i = 0; i < count; i ++)
    {
        if(cal_func_mapping_table[i].cal_type == cal_type)
        {
            ret = cal_func_mapping_table[i].result_get(num1,num2);
            break;//跳出循環
        }
    }

    if(i == count)
    {
        printf("error cal_type!\r\n");//暫且支持這麼多類型的計算,
    }

    return ret;
}

int main(int argc,char *argv[])
{
    double val = 0.0;

    val = result_get_version2(10,20,'+');
    printf("value = %f\r\n",val);
    val = result_get_version2(10,20,'-');
    printf("value = %f\r\n",val);
    val = result_get_version2(10,20,'*');
    printf("value = %f\r\n",val);
    val = result_get_version2(10,20,'/');
    printf("value = %f\r\n",val);
    val = result_get_version2(10,20,'2');
    printf("value = %f\r\n",val);
    val = result_get_version2(10,20,'3');
    printf("value = %f\r\n",val);
}

運行結果:

對比這兩個版本的代碼:

版本一:如果想要增加功能,只能在result_get_version1函數裏面增加if-else條件判斷,對擴展一點也不友好。

版本二:如果想要增加功能,首先要新增一個運算的計算函數,然後在cal_func_mapping_table表中增加對應的運算即可,根本就不需要修改result_get_version2裏面的函數。符合了程序的設計原則:對擴展開發,對修改關閉!

版本二關鍵設計點:

利用映射的方式進行處理條件分支。

首先:定義一個結構體(具體問題具體分析):

typedef struct CAL_FUNC_MAPPING
{
    char cal_type;//用來匹配輸入的運算
    double (*result_get)(double num1,double num2);//回調函數來指向用戶寫的運算函數
}cal_func_mapping;

其次:定義一個映射表(其實就是一個cal_func_mapping類型的數組了)~~~

static cal_func_mapping cal_func_mapping_table[] =
{
    {
        '+',
        result_get_add
    },
    {
        '-',
        result_get_sub
    },
    {
        '*',
        result_get_mul
    },
    {
        '/',
        result_get_div
    },
    {
        '2',
        result_get_2
    },
    {
        '3',
        result_get_3
    }
};

最後:在實際調用的計算函數來根據計算類型,選擇相應的計算函數就OK了

double result_get_version2(double num1,double num2,char cal_type)
{
    int count = sizeof(cal_func_mapping_table) / sizeof(cal_func_mapping);
    int i = 0;
    double ret = 0.0;

    for(i = 0; i < count; i ++)
    {
        if(cal_func_mapping_table[i].cal_type == cal_type)
        {
            ret = cal_func_mapping_table[i].result_get(num1,num2);
            break;//跳出循環
        }
    }

    if(i == count)
    {
        printf("error cal_type!\r\n");//暫且支持這麼多類型的計算,
    }

    return ret;
}

 

PS:如果C語言支持Python的裝飾器和閉包函數的話,就只需要增加新的運算計算函數了。至於Python的裝飾器和閉包函數是什麼玩意???自己百度百度哈~~我就不誤人子弟了。

如果這篇文章對你有幫助,就點個贊再走吧~~~哈哈哈~~~~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章