重构-改善既有代码的设计:简化条件表达式(七)
你有一個復雜的條件語句。從if、then、else三個段落中分別提煉出獨立函數。
程序之中,復雜的條件邏輯是最常導致復雜度上升的地點之一。你必須編寫代碼來檢查不同的條件分支、根據不同的分支做不同的事,然后,你很快就會得到一個相當長的函數。大型函數自身就會使代碼的可讀性下降,而條件邏輯則會使代碼更難閱讀。在帶有復雜條件邏輯的函數中,代碼(包括檢查條件分支的代碼和真正實現功能的代碼)會告訴你發生的事,當常常讓你弄不清為什么會發生這樣的事,這就說明代碼的可讀性的確大大降低了。
???????和任何大塊頭代碼一樣,你可以將它分解為多個獨立函數,根據每個小塊代碼的用途,為分解的新函數命名,并將原函數中對應的代碼改為調用新建函數,從而更清楚的表達自己的意圖。對于條件邏輯,將每個分支條件分解為新函數還可以給你帶來更多好處:可以突出條件邏輯,更清楚地表明每個分支的作用,并且突出每個分支的原因。
2.Consolidate Conditional Expression 合并條件表達式
你有一系列條件測試,都得到相同結果。將這些測試合并為一個條件表達式,并將這個條件表達式提煉為一個獨立函數。
有時你會發現這樣一串條件檢查:檢查條件各不相同,最終行為卻一致。如果發現這種情況,就應該使用“邏輯或”和“邏輯與”將它們合并為一個條件表達式。
???????之所以要合并條件表達式,有2個重要原因。首先,合并后的條件代碼會告訴你“實際上只有一次條件檢查,只不過有多個并列條件需要檢查而已”,從而使這一次檢查的用意更清晰。當然,合并前和合并后的代碼有著相同的結果,但原先代碼傳達出的信息卻是“這里有一些各自獨立的條件測試,它們只是恰好同時發生”。其次,這項重構往往可以為你使用Extract Method(提煉方法)做好準備。將檢查條件提煉成一個獨立函數對于厘清代碼意義非常有用,因為它把描述“做什么“的語句換成了“為什么這樣做”。
???????條件語句的合并理由也同時指出了不要合并的理由:如果你認為這些條件檢查的確彼此獨立,的確不應該被視為同一次檢查,那么就不要使用本項重構。因為在這種情況下,你的代碼已經清晰表達出自己的意義。
3.Consolodate Duplicate Conditional Fragments 合并重復的條件片段
在條件表達式的每個分支上有著相同的一段代碼。將這段重復代碼移到條件表達式之外。
一組條件表達式的所有分支都執行了相同的某段代碼。你應該將這段代碼搬移到表達式外面。這樣,代碼才能更清楚地表明哪些東西隨條件變化而變化、哪些東西保持不變。
4.Remove Control Flag 移除控制標記
在一系列布爾表達式中,某個變量帶有“控制標記’的作用。
以break或return語句取代控制標記。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
在一系列條件表達式中,常常會看到用以判斷何時停止條件檢查的控制標記。這樣的標記帶來的麻煩超過了它所帶來的便利。人們之所以會使用這樣的控制標記,因為結構化編程原則告訴他們:每個子程序只能有一個入口和出口。“單一出口“原則會讓你在代碼中加入讓人討厭的控制標記,大大降低條件表達式的可讀性。這就是編程語言提供break和continue語句的原因:用它們跳出復雜的條件語句。去掉控制標記所產生的效果往往讓你大吃一驚:條件語句真正的用途會清晰得多。
5.Replace Nested Conditional with Guard Clauses 以衛語句取代嵌套條件表達式
函數中的條件邏輯使人難以看清正常的執行途徑。使用衛語句表現所有特殊情況。
條件表達式通常有2種表現形式。第一:所有分支都屬于正常行為。第二:條件表達式提供的答案中只有一種是正常行為,其他都是不常見的情況。
???????這2類條件表達式有不同的用途。如果2條分支都是正常行為,就應該使用形如if…..else…..的條件表達式;如果某個條件極其罕見,就應該單獨檢查該條件,并在該條件為真時立刻從函數中返回。這樣的單獨檢查常常被稱為“衛語句”。
?????? Replace Nested Conditional with Guard Clauses?(以衛語句取代嵌套條件表達式)的精髓是:給某個分支以特別的重視。它告訴閱讀者:這種情況很罕見,如果它真的發生了,請做一些必要的整理工作,然后退出。
???????“每個函數只能有一個入口和一個出口”的觀念,根深蒂固于某些程序員的腦海里。現今的編程語言都會強制保證每個函數只有一個入口,至于“單一出口”規則,其實不是那么有用。保持代碼清晰才是最關鍵的:如果單一出口能使這個函數更清晰易讀,那么就使用單一出口;否則就不必這么做。
(衛語句就是把復雜的條件表達式拆分成多個條件表達式,比如一個很復雜的表達式,嵌套了好幾層的if - then-else語句,轉換為多個if語句,實現它的邏輯,這多條的if語句就是衛語句.)
6.Replace Conditional with Polymorphism 以多態取代條件表達式
你手上一個條件表達式,它根據對象類型的不同而選擇不同的行為。將這個條件表達式的每個分支放進一個子類的覆寫函數中,然后將原始函數聲明為抽象函數。
? ? ? 多態的最根本的好處是:如果你需要根據對象的不同類型而采取不同的行為,多態使你不必編寫某些的條件表達式。
???????正因為有了多態,所以你會發現:“類型嗎的switch語句”以及 ”基于類型名稱的if-then-else語句“在面向對象程序中很少出現。
???????多態能夠給你帶來很多好處。如果同一組條件表達式在程序的許多地點出現,那么使用多態的收益是最大的。使用條件表達式時,如果你想添加一種新類型,就必須查找并更新所有條件表達式。但如果使用多態,只需建立一個新的子類,并在其中提供適當的函數就行了。類的用戶不需要了解這個子類,這就大大降低了系統各部分之間的依賴,使系統升級更加容易。
7.?Introduce Null Object 引入Null對象
你需要再三檢查某對象是否為null。將null值替換為null對象。
多態的最根本好處在于:你不必再向對象詢問“你是什么類型”而后根據得到的答案調用對象的某個行為-你只管調用該行為就是了,其他的一切多態機制會為你安排妥當。當某個字段內容是null時,多態可扮演另一個較不直觀的用途。
8.?Introduce Assertion 引入斷言
某一段代碼需要對程序狀態做出某種假設。以斷言明確表現這種假設。
常常會有這樣一段代碼:只有當某個條件為真時,該段代碼才能正常運行。
???????這樣的假設通常并沒有在代碼中明確表現出來,你必須閱讀整個算法才能看出。有時程序員會以注釋寫出這樣的假設。可以使用斷言明確標明這些假設。
???????斷言是一個條件表達式,應該總是為真。如果它失敗,不是程序員犯了錯誤。因此斷言的失敗應該導致一個非受控異常。斷言絕對不能被系統的其他部分使用。實際上,程序最后的成品往往將斷言刪除。因此,標記“某個東西是個斷言”是很重要的。
???????斷言可以作為交流與調試的輔助。在交流的角度上,斷言可以幫助程序閱讀者了解代碼所做的假設;在調試的角度上,斷言可以在距離bug最近的地方抓住它們。
總結
以上是生活随笔為你收集整理的重构-改善既有代码的设计:简化条件表达式(七)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 重构与模式:改善代码三部曲中的第三部
- 下一篇: 重构-改善既有代码的设计:简化函数调用