如何優化程式碼中大量的if/else,switch/case?

深夜裡的程式猿發表於2019-04-30

前言

隨著專案的迭代,程式碼中存在的分支判斷可能會越來越多,當裡面涉及到的邏輯比較複雜或者分支數量實在是多的難以維護的時候,我們就要考慮下,有辦法能讓這些程式碼變得更優雅嗎?

正文

使用列舉

這裡我們簡單的定義一個表示狀態的列舉。

public enum Status {
    NEW(0),RUNNABLE(1),RUNNING(2),BLOCKED(3),DEAD(4);

    public int statusCode;

    Status(int statusCode){
        this.statusCode = statusCode;
    }
}
複製程式碼

那麼我們在使用的時候就可以直接通過列舉呼叫了。

int statusCode = Status.valueOf("NEW").statusCode;
複製程式碼

優雅的解決了下面程式碼賦值的方式

if(param.equals("NEW")){
    statusCode = 0;
}else if(param.equals("RUNNABLE")){
    statusCode = 1;
}
...
複製程式碼

善用 Optional

在專案中,總少不了一些非空的判斷,可能大部分人還是如下的用法

if(null == user){
    //action1
}else{
    //action2
}
複製程式碼

這時候該掏出Optional這個祕密武器了,它可以讓非空校驗更加優雅,間接的減少if操作。沒了解過Optional的同學可自行Google,這裡就不再贅述。

Optional<User> userOptional = Optional.ofNullable(user);
userOptional.map(action1).orElse(action2);
複製程式碼

上面的程式碼跟第一段是等效的,通過一些新特性讓程式碼更加緊湊。

表驅動法

來自Google的解釋:表驅動法是一種程式設計模式,它的本質是,從表裡查詢資訊來代替邏輯語句(if,case)。下面看一個案例,通過月份來獲取當月的天數(僅作為案例演示,獲取2月份的資料不嚴謹),普通做法:

int getMonthDays(int month){
	switch(month){
		case 1:return 31;break;
		case 2:return 29;break;
		case 3:return 31;break;
		case 4:return 30;break;
		case 5:return 31;break;
		case 6:return 30;break;
		case 7:return 31;break;
		case 8:return 31;break;
		case 9:return 30;break;
		case 10:return 31;break;
		case 11:return 30;break;
		case 12:return 31;break;
		defaultreturn 0;
	}
}
複製程式碼

表驅動法實現方式

int monthDays[12] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
int getMonthDays(int month){
	return monthDays[--month];
}
複製程式碼

其實這裡的表就是陣列而已,通過直接查詢陣列來獲得需要的資料,那麼同理,Map之類的容器也可以成為我們程式設計概念中的表。

Map<?, Function<?> action> actionsMap = new HashMap<>();

// 初試配置對應動作
actionsMap.put(value1, (someParams) -> { doAction1(someParams)});
actionsMap.put(value2, (someParams) -> { doAction2(someParams)});
actionsMap.put(value3, (someParams) -> { doAction3(someParams)});
 
// 省略 null 判斷
actionsMap.get(param).apply(someParams);
複製程式碼

通過Java8的lambda表示式,我們把需要執行東西存進value中,呼叫的時候通過匹配key的方式進行。

提前判斷返回

在之前的文章《優化程式碼中的“壞味道”》裡也有提過,如下語句


if(condition){
   //dost
}else{
   return ;
}
複製程式碼

改為

if(!condition){
   return ;
}
//dost
複製程式碼

避免一些不必要的分支,讓程式碼更精煉。

其他方法

除了上面提到的方法,我們還可以通過一些設計模式,例如策略模式,責任鏈模式等來優化存在大量if,case的情況,其原理會和表驅動的模式比較相似,大家可以自己動手實現一下,例如我們在Netty的使用過程中,可能會出現需要大量判斷不同的命令去執行對應動作的場景。

ServerHandler.java

if(command.equals("login")){
    //執行登入
}else if(command.equals("chat")){
    //聊天
}else if(command.equals("broadcast")){
    //廣播資訊
}
....
複製程式碼

該如何處理呢?這裡先賣個關子,大家可以先思考一下,筆記後續會寫一些關於Netty實現IM的文章,到時候會詳細介紹。

結語

最後要明確一點,不是所有的if/else,switch/case都需要優化,當我們發現有“痛點”或者“聞到程式碼有壞味道”再來優化才是最好的,不然你可能會寫了一個從不擴充套件的可擴充套件程式碼,所有的優化都是為了更好的迭代專案,更好的服務於業務,而不是為了優化而優化。


公眾號博文同步Github倉庫,有興趣的朋友可以幫忙給個Star哦,碼字不易,感謝支援。

github.com/PeppaLittle…

推薦閱讀

如何提高使用Java反射的效率?
Java日誌正確使用姿勢
大白話搞懂什麼是同步/非同步/阻塞/非阻塞
論JVM爆炸的幾種姿勢及自救方法

有收穫的話,就點個贊吧

關注「深夜裡的程式猿」,分享最乾的乾貨

如何優化程式碼中大量的if/else,switch/case?

相關文章