策略列舉:消除在專案裡大批量使用if-else的正確姿勢

朱季謙發表於2021-03-26

文/朱季謙

想起剛開始接觸JAVA程式設計的時候,若遇到大量流程判斷語句,幾乎滿屏都是if-else語句,多得讓自己都忘了哪裡是頭,哪裡是尾,但是,縱然滿屏是if-else,但彼時也沒有覺得多彆扭。等到程式設計能力漸漸提升之後,再回過頭去看曾經寫過的滿屏if-else時,腦海裡只有一個畫面,全都是翔。

那麼,如何消除在專案裡大量使用if-else呢?

網路上有很多解決思路,有工廠模式、策略模式、甚至是規則引擎(這個太重了吧)......

這些,都有一個共同的缺點,即使用起來還是過於繁重了。雖說避免出現過多的if-else,但是,卻增加很多額外的類,我總覺得,很不實用,只能當做某種模式的學習即可。

真正在專案中能替換大量if-else語句,且具備較好的可讀性與擴充套件性的,我比較推薦使用策略列舉來消除if-else。

假如有這樣一個需求,實現一週七天內分別知道要做事情的備忘功能,那麼,用if-else,可能是會這樣實現——

 1 //if-else判斷
 2 public String getToDoByIfElse(String day){
 3     if("Monday".equals(day)){
 4         return "今天上英語課";
 5     }else if("Tuesday".equals(day)){
 6         return "今天上語文課";
 7     }else if("Wednesday".equals(day)){
 8         return "今天上數學課";
 9     }else if("Thursday".equals(day)){
10         return "今天上音樂課";
11     }else if{
12         return "今天上程式設計課";
13     }else{
14         ......
15     }
16 }

若要改成策略列舉模式,可直接這樣,首先,先定義一個呼叫方法,假如傳進的是星期一,即引數"Monday",在下面方法裡,通過DayEnum.valueOf("Monday")可獲取其列舉屬性,這裡應該得到的是Monday——

//策略列舉判斷,呼叫方法getToDoByEnum
public String getToDoByEnum(String day){
    CheckDay checkDay=new CheckDay();
    return checkDay.day(DayEnum.valueOf(day));
}

接下來,CheckDay()方法裡會做一個策略匹配,根據上面傳進來的DayEnum.valueOf("Monday"),即得到了列舉Monday,那麼,在這個方法裡,就會執行Monday.toDo()——

public class CheckDay {
    public String day( DayEnum dayEnum) {
        return dayEnum.toDo();
    }
}

也就是執行Monday裡的toDo(),該列舉屬性當中實現了toDo()方法——

public enum DayEnum {
    Monday {
        @Override
        public String toDo() {
            return "今天上英語課";
        }
    },
    Tuesday {
        @Override
        public String toDo() {
            return "今天上語文課";
        }
    },
    Wednesday {
        @Override
        public String toDo() {
            return "今天上數學課";
        }
    },
    Thursday {
        @Override
        public String toDo() {
            return "今天上音樂課";
        }
    };
    public abstract String toDo();
}

總結一下,策略列舉就是列舉當中使用了策略模式,所謂的策略模式,即給你一把鑰匙,按照某種約定的方式,可以立馬被指引找到可以開啟的門。例如,我給你的鑰匙叫“Monday”,那麼,就可以通過約定方式dayEnum.toDo(),立馬找到列舉裡的Monday大門,然後進到門裡,去做想做的事toDo(),其中,每扇門後的房間都有不同的功能,但它們都有一個相同抽象功能——toDo(),即各房間共同地方都是可以用來做一些事情的功能,但具體可以什麼事情,就各有不同了。

這裡,會出現一種情況,即,假如有多個重複共同樣功能的判斷話,例如,在if-else裡,是這樣——

public String getToDoByIfElse(String day){
    if("Monday".equals(day)||"Tuesday".equals(day)||"Wednesday".equals(day)){
        return "今天上英語課";
    }else if("Thursday".equals(day)){
        ......
    }
}

那麼,在策略列舉下應該如何使用從而避免程式碼冗餘呢?

可以參考一下以下思路,設定一個內部策略列舉,將有相同功能的外部引用指向同一個內部列舉屬性,這樣即可實現呼叫重複功能了——

public enum DayEnum {
    //指向內部列舉的同一個屬性即可執行相同重複功能
    Monday("星期一", Type.ENGLISH),
    Tuesday("星期二", Type.ENGLISH),
    Wednesday("星期三", Type.ENGLISH),
    
    Thursday("星期四", Type.CHINESE);
    private final Type type;
    private final String day;
    DayEnum(String day, Type type) {
        this.day = day;
        this.type = type;
    }
    String toDo() {
        return type.toDo();
    }
    /**
     * 內部策略列舉
     */
    private enum Type {
        ENGLISH {
            @Override
            public String toDo() {
                return "今天上英語課";
            }
        },
        CHINESE {
            @Override
            public String toDo() {
                return "今天上語文課";
            }
        };
        public abstract String toDo();
    }
}

我很喜歡在大批量if-else裡使用策略列舉來消除替換,總而言之,使用策略列舉可以很靈活處理各種複雜判斷,且可讀性與擴充套件性都比較好,它更像是函數語言程式設計,而大批量if-else,則是程式導向了。因為,if-else是從上往下一個if接一個if判斷下去的,在各個if上打個斷點,debug下去,就明白了。

由此可知,若專案裡有大量的if-else話,著實是一件很影響效能的事情。

相關文章