【JAVA】優化if else的幾種方式

在代碼編寫初期,我們寫出來的代碼,脈絡清晰,結構簡單。可隨着bug或者新需求的出現,狀態變得越來越多,只能不停地加else來區分,久而久之,判斷的次數越來越多,嵌套的層數也越來越深,變得難以維護。

當我們狠下心來決定改造時,有哪些方法能夠優化if else結構呢?


第一種方法:提前return,減少else判斷

優化前:

    private int handlePre1(boolean flag) {
        if (flag) {
            //do something
        } else {
            //do something
            return -1;
        }
        return 0;
    }

優化後:

    private int handleAfter1(boolean flag) {
        if (!flag) {
            //do something
            return -1;
        }
        //do something
        return 0;
    }

這個做法,將可以return的狀態提到if中,直接幹掉了else!


第二種方法:使用三目運算符

優化前:

    private int handlePre2(boolean flag) {
        if (flag) {
            return 0;
        } else {
            return 1;
        }
    }

優化後:

    private int handleAfter2(boolean flag) {
        return flag ? 0 : 1;
    }

這種方式侷限性比較大,但對於那些依據狀態直接返回某個值的情況非常適用,優化後代碼非常簡潔。如果三目運算符中還嵌套一層三目運算符,建議就不要使用這種方式了。


第三種方法:使用Optional

我們在代碼中,經常需要判斷某個對象是否爲null,不爲null後纔會進行接下來的操作,好在java8爲我們提供了Optional類。

優化前:

        User user = new User();
        int age = 20;
        if (user != null) {
            age = user.getAge();
        }

優化後:

        User user = new User();
        Optional<User> optionalUser = Optional.ofNullable(user);
        Integer age = optionalUser.map(User::getAge).orElse(20);

Optional讓非空校驗更加有優雅,代碼層面減少了if判斷,其實Optional在底層爲我們封裝好了if null判斷而已。


第四種方法:使用switch case

優化前:

    private String getCn(String en) {
        String cn;
        if ("student".equals(en)) {
            cn = "學生";
        } else if ("teacher".equals(en)) {
            cn = "教師";
        } else {
            cn = "未知";
        }
        return cn;
    }

同樣,我們可以借鑑方式一提前return,來代替else

    private String getCnByEarlyReturn(String en) {
        if ("student".equals(en)) {
            return "學生";
        }
        if ("teacher".equals(en)) {
            return "教師";
        }
        return "未知";
    }

如果這裏面的情況很多,我們可能依然要判斷多次,那我們用switch改寫。

優化後:

    private String getCnBySwitch(String en) {
        switch (en) {
            case "student":
                return "學生";
            case "teacher":
                return "教師";
            default:
                return "未知";
        }
    }

如果單從效率來看,之前的時間複雜度爲O(n),switch的複雜度爲O(1),本質上swicth是通過比對String的哈希碼來實現的。


第五種方法:使用枚舉

如果上一例中的student、teacher是常量的話,最好是定義在枚舉裏。

    public enum CnEnum {

        STUDENT("student", "學生"),
        TEACHER("teacher", "教師"),
        UNKNOWN("unKnown", "未知");

        private String en;
        private String cn;

        public String getEn() {
            return en;
        }

        public String getCn() {
            return cn;
        }

        CnEnum(String en, String cn) {
            this.en = en;
            this.cn = cn;
        }

        static String of(String en) {
            for (CnEnum temp : CnEnum.values()) {
                if (temp.getEn().equals(en)) {
                    return temp.getCn();
                }
            }
            return CnEnum.valueOf("UNKNOWN").getCn();
        }
    }

最後這樣調用即可:

        String cn = CnEnum.of("student");

第六種方法:表驅動法

表驅動法是指我們可以將信息存在於表中,從表中取,而不必用if else邏輯去尋找,大可以將這裏的“表”理解爲一種容器。

例如我們可以將每月的天數存在數組中,需要時,直接從數組中獲取,而不是用if else輸出。

或者依據不同的字符串,執行不同的處理邏輯。if else版本如下:

        String profession = "student";
        if ("student".equals(profession)) {
           //do something
        } else if ("teacher".equals(profession)) {
            //do something
        } else if ("programmer".equals(profession)) {
            //do something
        }

在這種情況下,使用表驅動法後,可以讓Student、Teacher等類實現某一個共同的接口,在實現方法裏,書寫各自的邏輯。然後利用Spring強大的自動注入,會注入到Map<組件名稱,組件實例>的map裏,之後直接根據組件名稱來獲取到對應的實例,最後調用各自的邏輯。

這個例子可以先異步到我的另外一篇文章【SpringBoot】使用不同的策略動態地調用某個接口的實現類


第七種方法:策略模式+工廠模式

首先給定以下的一個場景,傳入一個職業,輸出職業的主要工作

一般我們會這麼寫:

        String profession = "student";
        if ("student".equals(profession)) {
            System.out.println("學習");
        } else if ("teacher".equals(profession)) {
            System.out.println("教書");
        } else if ("programmer".equals(profession)) {
            System.out.println("寫代碼");
        }
        //.......以後可能會加上各種各樣的職業

那麼,沒增加一種職業,就會增加一個if else判斷,代碼可讀性差,日後難以維護。

現在使用策略模式+工廠模式來改造它。

  • 首先定義一個職業接口,裏面定義一個輸出職責的方法
  • 之後每增加一種職業,都實現職業接口,實現輸出職責的方法(策略模式)
  • 接着定義一個工廠類,用來根據具體情況生產各種職業類(工廠模式)

職業接口:

public interface Profession {

    //輸出職責
    void output();

}

實現類:

public class Student implements Profession {

    @Override
    public void output() {
        System.out.println("學習");
    }

}


public class Teacher implements Profession {

    @Override
    public void output() {
        System.out.println("教書");
    }

}


public class Programmer implements Profession {

    @Override
    public void output() {
        System.out.println("寫代碼");
    }

}

工廠類:

public class ProfessionFactory {

    private static Map<String, Profession> map = new HashMap<>();
    //未知職業
    private static final Profession DEFAULT_PROFESSION = new DefaultProfession();

    static {
        map.put("student", new Student());
        map.put("teacher", new Teacher());
        map.put("default", DEFAULT_PROFESSION);
    }

    public static Profession getProfession(String s) {
        Profession profession = map.get(s);
        return profession == null ? DEFAULT_PROFESSION : profession;
    }

    static class DefaultProfession implements Profession {

        @Override
        public void output() {
            System.out.println("職責未知");
        }

    }
}

優化後:

        Profession profession = ProfessionFactory.getProfession("student");
        profession.output();

之後每增加一種職業,只要實現Profession接口就好,並在ProfessionFactory中註冊自己。使用策略模式+工廠模式,再也不需要爲日益增長的if else頭疼了。

當然,這裏的key值,可以設置爲常量。


總結

過多的if else嚴重降低可讀性與可維護性,所以在一開始,就要依據實際情況,靈活地選擇處理方式。

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