消除if else, 讓你的程式碼看起來更優雅

2020-10-12 18:00:23

欄目介紹如何消除if else, 讓你的程式碼看起來更優雅,一起來看看吧。

前言

應該有不少同學有遇到過充斥著if else的程式碼,面對這樣的一團亂麻,簡單粗暴地繼續增量修改常常只會讓複雜度越來越高,可讀性越來越差。那麼是時候重構了,花幾分鐘看看這篇文章, 說不定對你有一丟丟幫助。

場景一: 根據status顯示對應名稱

優化方案1:object物件

const statusStr = {  '1': '待付款',  '2': '待發貨',  '3': '已發貨',  '4': '交易完成',  '5': '交易關閉',  'default': '',}const getStatus = (status) =>{  return statusStr[status] || statusStr['default']}複製程式碼

將判斷條件作為物件的屬性名,將處理邏輯作為物件的屬性值,在按鈕點選的時候,通過物件屬性查詢的方式來進行邏輯判斷.

優化方案2: Map物件

const statusStr = new map([  '1': ['待付款'],  '2': ['待發貨'],  '3': ['已發貨'],  '4': ['交易完成'],  '5': ['交易關閉'],  'default': [''],])const getStatus = (status) =>{  let actions = statusStr.get(status) || statusStr.get('default')  return  actions[0];}複製程式碼

這樣寫用到了es6裡的Map物件,那麼Map物件和Object物件有什麼區別呢?

一個物件通常都有自己的原型,所以一個物件總有一個"prototype"鍵。 一個物件的鍵只能是字串或者Symbols,但一個Map的鍵可以是任意值。 你可以通過size屬性很容易地得到一個Map的鍵值對個數,而物件的鍵值對個數只能手動確認。

場景二:多個condition對應名稱

現在把問題升級一下, 以前按鈕點選時候只需要判斷status,現在還需要判斷使用者的身份:
「舉個栗子:」

const onButtonClick = (status,identity)=>{  if(identity == 'guest'){    if(status == 1){      //do sth    }else if(status == 2){      //do sth    }else if(status == 3){      //do sth    }else if(status == 4){      //do sth    }else if(status == 5){      //do sth    }else {      //do sth    }  }else if(identity == 'master') {    if(status == 1){      //do sth    }else if(status == 2){      //do sth    }else if(status == 3){      //do sth    }else if(status == 4){      //do sth    }else if(status == 5){      //do sth    }else {      //do sth    }  }}複製程式碼

上面的例子我們可以看到,當你的邏輯升級為二元判斷時,你的判斷量會加倍,你的程式碼量也會加倍,這時怎麼寫更清爽呢?

優化方案1: 將condition用字元拼接形式存在Map物件裡

const actions = new Map([  ['guest_1', ()=>{/*do sth*/}],  ['guest_2', ()=>{/*do sth*/}],  ['guest_3', ()=>{/*do sth*/}],  ['guest_4', ()=>{/*do sth*/}],  ['guest_5', ()=>{/*do sth*/}],  ['master_1', ()=>{/*do sth*/}],  ['master_2', ()=>{/*do sth*/}],  ['master_3', ()=>{/*do sth*/}],  ['master_4', ()=>{/*do sth*/}],  ['master_5', ()=>{/*do sth*/}],  ['default', ()=>{/*do sth*/}],])const onButtonClick = (identity,status)=>{  let action = actions.get(`${identity}_${status}`) || actions.get('default')  action.call(this)}複製程式碼

上述程式碼核心邏輯是:把兩個條件拼接成字串,並通過以條件拼接字串作為鍵,以處理常式作為值的Map物件進行查詢並執行,這種寫法在多元條件判斷時候尤其好用。

優化方案2: 將condition用字元拼接形式存在Object物件裡

const actions = {  'guest_1':()=>{/*do sth*/},  'guest_2':()=>{/*do sth*/},  //....}const onButtonClick = (identity,status)=>{  let action = actions[`${identity}_${status}`] || actions['default']  action.call(this)}複製程式碼

優化方案3: 將condition用Object物件形式存在Map物件裡

可能用查詢條件拼成字串有點彆扭,那還有一種方案,就是用Map物件,以Object物件作為key:

const actions = new Map([  [{identity:'guest',status:1},()=>{/*do sth*/}],  [{identity:'guest',status:2},()=>{/*do sth*/}],  //...])const onButtonClick = (identity,status)=>{  let action = [...actions].filter(([key,value])=>(key.identity == identity && key.status == status))  action.forEach(([key,value])=>value.call(this))}複製程式碼

場景三:根據status做出相應操作

「舉個栗子:」

function init () {    if (isAnswer === 1) {        if (isOldUser === 1) {            // ...        } else if (isOldUser === 2) {            // ...        }    } else if (isAnswer === 2) {        if (isOldUser === 1) {            // ...        } else if (isOldUser === 2) {            // ...        }    } else if (isAnswer === 3) {        if (isOldUser === 1) {            // ...        } else if (isOldUser === 2) {            // ...        }    }}複製程式碼

優化方案1: 查詢表,職責鏈查詢表

const rules = [    {        match (an, old) {if (an === 1) {return true}},        action (an, old) {        if (old === 1) {// ...}         else if (old === 2) {// ...}        }    },    {        match (an, old) { if (an === 2) {return true } },        action (an, old) {            if (old === 1) {// ...}             else if (old === 2) {// ...}        }    },    {        match (an, old) {if (an === 3) {return true}},        action (an, old) {            if (old === 1) {// ...}             else if (old === 2) {// ...}        }    }]function init (an, old) {    for (let i = 0; i < rules.length; i++) {        // 如果返回true        if (rules[i].match(an, old)) {            rules[i].action(an, old)        }    }}init(isAnswer, isOldUser)複製程式碼

雖然可能看著是治標不治本,其實不然,init函數的複雜度大大的降低了。我們已經把控制流程的複雜邏輯,拆分到determineAction函數中

優化方案2: 函數語言程式設計

import R from 'ramda'var fn = R.cond([  [R.equals(0),   R.always('water freezes at 0°C')],  [R.equals(100), R.always('water boils at 100°C')],  [R.T,           temp => 'nothing special happens at ' + temp + '°C']]);fn(0); //=> 'water freezes at 0°C'fn(50); //=> 'nothing special happens at 50°C'fn(100); //=> 'water boils at 100°C'複製程式碼

利用ramda等函數語言程式設計庫解決這種問題,

場景四: 根據範圍去進行不同處理

「舉個栗子:」比如大家可能會遇到類似下面的需求:比如某平臺的信用分數評級,超過700-950,就是信用極好,650-700信用優秀,600-650信用良好,550-600信用中等,350-550信用較差。

function showGrace(grace) {
    let _level='';
    if(grace>=700){
        _level='信用極好'
    }
    else if(grace>=650){
        _level='信用優秀'
    }
    else if(grace>=600){
        _level='信用良好'
    }
    else if(grace>=550){
        _level='信用中等'
    }
    else{
        _level='信用較差'
    }
    return _level;
}

優化方案1: 用look-up表,把設定資料和業務邏輯分離

function showGrace(grace,level,levelForGrace) {
    for(let i=0;i<level.length;i++){
        if(grace>=level[i]){
            return levelForGrace[i];
        }
    }
    //如果不存在,那麼就是分數很低,返回最後一個
    return levelForGrace[levelForGrace.length-1];
}
let graceForLevel=[700,650,600,550];
let levelText=['信用極好','信用優秀','信用良好','信用中等','信用較差'];

小結

很多情況下我們都可以使用更靈活的方式去替代if else以及switch, 但也不是所有的if else都需要替代, 視情況而定。

更多相關免費學習:(視訊)

以上就是消除if else, 讓你的程式碼看起來更優雅的詳細內容,更多請關注TW511.COM其它相關文章!