過多if-else分支的優化
有一些朋友說if-else過多的分支可以使用switch或者責任鏈模式等等方式來優化。確實,這是一個小問題,不過我們還是可以整理一下這個小問題的重構方式。
為什麼要優化?
你沒有看錯。這是要放在第一條談論的。
有許多人會說,疊起來一堆if-else分支,程式碼就不優雅了。可是,怎樣去定義“優雅”的概念呢?再退一步說,即便不“優雅”,又有什麼問題?
對於這樣一段再普通不過的程式碼:
1 2 3 4 5 6 7 8 |
int
code;
if ( "Name" .equals(str))
code
= 0 ;
else
if ( "Age" .equals(str))
code
= 1 ;
else
if ( "Address" .equals(str))
code
= 2 ;
...
|
可以有好多種重構方式,但是使用這樣的程式碼,雖然簡陋,但在大多數情況下,並不會影響什麼,比如,對可維護性沒有影響。當然,如果你發現其中確有不好的一面,那就要考慮重構它。換言之,通常你首先要說出某段程式碼的問題(比如,你覺得這段程式碼不符合開閉原則,因為你希望保持這段程式碼閉合穩定),那麼才去存在重構的必要,而不要總是使用“優雅”和“簡潔”搪塞疑問。幾乎所有的書上都說要寫出優雅的、簡潔的程式碼,這本身無可厚非,但是事物需要使用自己的判斷,可不要被習慣性地洗了腦。
在我前一家公司,是典型的通訊和傳統軟體的公司,程式碼質量普遍不錯,但是很多時候,會看到許許多多不夠優雅的程式碼——也許你覺得不夠簡潔、美觀,但是下程式碼嚴謹、清晰,我覺得這就很好。反之,某一些精巧的設計,可能會帶來可閱讀性和可理解性下降的問題。
尋找代替分支判斷的方式
接下去我們再來考慮怎麼樣去重構優化過多的if-else分支。
程式邏輯最基本的組成就是分支、判斷和迴圈。而過多if-else正是由於在某一個變化的點上,有許多判斷條件和結果分支造成的。所以最基本的解決辦法就是把多個判斷條件合成一個,也就是把若干個分支合成一個。
但是在大多數情況下,條件判斷的分支都是無法合併的。所以,我們需要把這個變化點通過別的途徑封裝起來,而不是採用if-else。
1. 用一個Map可以做到,if-else的變化點使用Map的get方法來代替:
1 2 3 4 5 6 |
Map
typeCodeMap = new
HashMap(); typeCodeMap.put( "Name" ,
0 );
typeCodeMap.put( "Age" ,
1 );
typeCodeMap.put( "Address" ,
2 );
...
int
code = typeCode.get(type);
|
2. 列舉:
1 2 3 4 5 6 7 8 9 10 11 |
public
enum
Codes {
Name( 0 ),
Age( 1 ),
Address( 2 );
public
int
code;
Codes( int
code){
this .code
= code;
}
}
//使用:
int
code = Codes.valueOf(str).code;
|
3. 多型:
1 2 |
ICode
iCode = (ICode)Class.forName( "com.xxx."
+ str).newInstance();
int
code = iCode.getCode();
|
當然,如果僅考慮從String轉向int這樣的轉換,用這樣的方式來簡化分支判斷邏輯,這個方式、這個例子不是很恰當。當然,這樣的方式經常被用來做從字串到具體物件的轉換。
還有一些朋友說的這個模式那個模式來解決多if-else的問題,這些都是正確的,當然本質上也無一例外基於多型來實現的,所以我就不提及了。這些都不錯,至少比那些老說用switch來代替if-else的有價值多了 :)
最後,對於如此小的一個問題,我要補充說明的一點是,看不得大片if-else和看不得大片new關鍵字一樣,我覺得這是許多Java程式設計師的既有觀念或者說習慣,甚至通病——這並不好。Java最有價值的地方不是它的語義語法也不是它的虛擬機器跨平臺和有多高效能,而在於它的社群它的無比豐富的類庫,在於使用它的人可以從設計上和巨集觀上去思考問題。但是Java程式設計師,也包括我在內,很容易把這條路走得過於極端,比如遍地的Factory,比如漫山遍野的配置,比如永遠也不會被複用的可複用程式碼,比如永遠也不會被擴充套件的可擴充套件程式碼,還比如從前到後由內到外的分層,一層又一層。相對於這些方面無止境的追求,我們還是專注於要解決的問題,多寫一些清晰可用的程式碼吧。