在代碼編寫初期,我們寫出來的代碼,脈絡清晰,結構簡單。可隨着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嚴重降低可讀性與可維護性,所以在一開始,就要依據實際情況,靈活地選擇處理方式。