轉帖|行業資訊|編輯:龔雪|2016-06-29 09:53:08.000|閱讀 272 次
概述: 本文將會向您介紹幾種程序中減少使用if語句的方法。
# 界面/圖表報表/文檔/IDE等千款熱門軟控件火熱銷售中 >>
大約十年前,我聽說了反 if 的活動,覺得這個概念非常荒謬。如果不用if語句,又怎么能寫出有用的程序呢?這簡直太荒謬了。
但之后你會開始思考:是否還記得上周你拼命想讀懂的深度嵌套代碼?糟透了對么?要是有辦法能簡化它該多好。
反 if 活動的網站上沒給出多少實用性建議,因此在本文中,作者將會提供一系列模式,也許你會用得上。但首先我們來關注一下if語句到底造成了什么問題。
if語句的第一個問題在于,通常出現if語句的代碼很容易越改越糟。我們試著寫個新的if語句:
public void theProblem(boolean someCondition) { // SharedState if(someCondition) { // CodeBlockA } else { // CodeBlockB } }
這時候還不算太糟,但已經存在一些問題了。在閱讀這段代碼時,我必須得去查看對同一個SharedState來說,CodeBlockA和CodeBlockB有什么改動。最開始這段代碼很容易閱讀,但隨著CodeBlock越來越多,耦合越來越復雜之后,就會很難讀。
上面這種CodeBlock進一步嵌套if語句與本地return的濫用情況也很常見,很難搞懂業務邏輯是選擇了哪種路徑。
if 語句的第二個問題在于:復制時會有問題,也就是說,if語句缺失domain的概念。很容易由于在不需要的情況下,由于將內容放在一起而增加耦合性,造成代碼難讀難改。
而第三個問題在于:開發者必須在頭腦中模擬執行實現情況——你得讓自己變成一臺小型電腦,從而造成腦細胞浪費。開發者的精力應當用來思考如何解決問題,而不是浪費在如何將復雜的代碼分支結構編織在一起之上。
雖然想要直截了當地寫出替代方案,但首先我得強調這句話:
凡事中庸而行,尤其是中庸本身
if 語句通常會讓代碼更加復雜,但這不代表我們要完全拋棄if語句。我曾經看到過一些非常糟糕的代碼,只是為了消除所有的if語句而刻意避開if語句。我們想要繞開這個誤區, 下面我給出的每種模式,都會給出使用范圍。
單獨的if語句如果不復制到其他地方,也許是不錯的句子。在復制if語句時,我們會希望預知危險的第六感起效。
在代碼庫之外,在與危險的外部世界交流時,我們會想要驗證incoming response,并根據其作出相應的修改。但在自己的代碼庫中,由于有可靠的gatekeeper把關,我覺得這是個很好的機會,我們可以嘗試使用簡單、更為豐富與強大的替代方案來實現。
背景: 有方法在修改行為時使用了boolean
Java代碼
public void example() { FileUtils.createFile("name.txt", "file contents", false); FileUtils.createFile("name_temp.txt", "file contents", true); } public class FileUtils { public static void createFile(String name, String contents, boolean temporary) { if(temporary) { // save temp file } else { // save permanent file } } }
問題: 在看到這段代碼時,實際上你是將兩個方法捆綁到一起,布爾參數的出現讓你有機會在代碼中定義一個概念。
適用范圍: 通常看到這種情況,如果在編譯時我們可以算出代碼要采用哪種路徑,就可以放心使用這種模式。
解決方案: 將這個方法拆分成兩個新的方法,然后if就不見了。
Java代碼
public void example() { FileUtils.createFile("name.txt", "file contents"); FileUtils.createTemporaryFile("name_temp.txt", "file contents"); } public class FileUtils { public static void createFile(String name, String contents) { // save permanent file } public static void createTemporaryFile(String name, String contents) { // save temp file } }
背景: 根據類型switch時
Java代碼
public class Bird { private enum Species { EUROPEAN, AFRICAN, NORWEGIAN_BLUE; } private boolean isNailed; private Species type; public double getSpeed() { switch (type) { case EUROPEAN: return getBaseSpeed(); case AFRICAN: return getBaseSpeed() - getLoadFactor(); case NORWEGIAN_BLUE: return isNailed ? 0 : getBaseSpeed(); default: return 0; } } private double getLoadFactor() { return 3; } private double getBaseSpeed() { return 10; } }
問題: 在添加新的類型時,我們必須要記得更新switch語句,此外隨著不同bird的概念添加進來,bird類的凝聚力越來越糟。
適用范圍:根據類型做單次切換是可行的,如果switch太多,在添加新類型時如果忘記更新現有隱藏類型中的所有switch,就會導致bug出現,8thlight博客關于這種情況有詳細描述。
解決方案: 使用多態,添加新類型時大家都不會忘記添加相關行為。 注意:上例為了簡潔只寫了一個方法,但在有多個switch時更有用。
Java代碼
public abstract class Bird { public abstract double getSpeed(); protected double getLoadFactor() { return 3; } protected double getBaseSpeed() { return 10; } } public class EuropeanBird extends Bird { public double getSpeed() { return getBaseSpeed(); } } public class AfricanBird extends Bird { public double getSpeed() { return getBaseSpeed() - getLoadFactor(); } } public class NorwegianBird extends Bird { private boolean isNailed; public double getSpeed() { return isNailed ? 0 : getBaseSpeed(); } }
背景: 在計算布爾表達式時,包含if語句樹
Java代碼
public boolean horrible(boolean foo, boolean bar, boolean baz) { if (foo) { if (bar) { return true; } } if (baz) { return true; } else { return false; } }
問題: 這種代碼會導致開發者必須用大腦來模擬計算機對方法的處理。
適用范圍:很少有不適用的情況,像這樣的代碼可以合成一行,或者拆成不同的部分。
解決方案: 將if語句樹合成單個表達式。
Java代碼
public boolean horrible(boolean foo, boolean bar, boolean baz) { return foo && bar || baz; }
背景:在調用一些其他代碼時,無法確保路徑是成功的。
Java代碼
public class Repository { public String getRecord(int id) { return null; // cannot find the record } } public class Finder { public String displayRecord(Repository repository) { String record = repository.getRecord(123); if(record == null) { return "Not found"; } else { return record; } } }
問題: 這類if語句增加了處理同一個對象或者數據結構的時間,其中包含隱藏耦合——null的情況。其它對象可能會返回其他代表沒有結果的magic value。
適用范圍:最好將這類if語句放在一個地方,由于不會重復,我們就能將為空對象的magic value刪除。
解決方案:針對被調用代碼,給出應對策略。Ruby的Hash#fetch就是很好的案例,Java也用到了類似的方法。這種模式也可以用在刪除例外情況時。
Java代碼
private class Repository { public String getRecord(int id, String defaultValue) { String result = Db.getRecord(id); if (result != null) { return result; } return defaultValue; } } public class Finder { public String displayRecord(Repository repository) { return repository.getRecord(123, "Not found"); } }
希望這些模式對你現在處理的問題有幫助。我在重構代碼增進理解時,發現這些方法都很有用。要記得并非所有if語句都是魔鬼,不過現代編程語言還有很多功能值得我們探索并使用。
本文來源:
本站文章除注明轉載外,均為本站原創或翻譯。歡迎任何形式的轉載,但請務必注明出處、不得修改原文相關鏈接,如果存在內容上的異議請郵件反饋至chenjj@fc6vip.cn