轉(zhuǎn)帖|行業(yè)資訊|編輯:龔雪|2016-06-29 09:53:08.000|閱讀 272 次
概述: 本文將會(huì)向您介紹幾種程序中減少使用if語(yǔ)句的方法。
# 界面/圖表報(bào)表/文檔/IDE等千款熱門軟控件火熱銷售中 >>
大約十年前,我聽(tīng)說(shuō)了反 if 的活動(dòng),覺(jué)得這個(gè)概念非常荒謬。如果不用if語(yǔ)句,又怎么能寫出有用的程序呢?這簡(jiǎn)直太荒謬了。
但之后你會(huì)開(kāi)始思考:是否還記得上周你拼命想讀懂的深度嵌套代碼?糟透了對(duì)么?要是有辦法能簡(jiǎn)化它該多好。
反 if 活動(dòng)的網(wǎng)站上沒(méi)給出多少實(shí)用性建議,因此在本文中,作者將會(huì)提供一系列模式,也許你會(huì)用得上。但首先我們來(lái)關(guān)注一下if語(yǔ)句到底造成了什么問(wèn)題。
if語(yǔ)句的第一個(gè)問(wèn)題在于,通常出現(xiàn)if語(yǔ)句的代碼很容易越改越糟。我們?cè)囍鴮憘€(gè)新的if語(yǔ)句:
public void theProblem(boolean someCondition) { // SharedState if(someCondition) { // CodeBlockA } else { // CodeBlockB } }
這時(shí)候還不算太糟,但已經(jīng)存在一些問(wèn)題了。在閱讀這段代碼時(shí),我必須得去查看對(duì)同一個(gè)SharedState來(lái)說(shuō),CodeBlockA和CodeBlockB有什么改動(dòng)。最開(kāi)始這段代碼很容易閱讀,但隨著CodeBlock越來(lái)越多,耦合越來(lái)越復(fù)雜之后,就會(huì)很難讀。
上面這種CodeBlock進(jìn)一步嵌套if語(yǔ)句與本地return的濫用情況也很常見(jiàn),很難搞懂業(yè)務(wù)邏輯是選擇了哪種路徑。
if 語(yǔ)句的第二個(gè)問(wèn)題在于:復(fù)制時(shí)會(huì)有問(wèn)題,也就是說(shuō),if語(yǔ)句缺失domain的概念。很容易由于在不需要的情況下,由于將內(nèi)容放在一起而增加耦合性,造成代碼難讀難改。
而第三個(gè)問(wèn)題在于:開(kāi)發(fā)者必須在頭腦中模擬執(zhí)行實(shí)現(xiàn)情況——你得讓自己變成一臺(tái)小型電腦,從而造成腦細(xì)胞浪費(fèi)。開(kāi)發(fā)者的精力應(yīng)當(dāng)用來(lái)思考如何解決問(wèn)題,而不是浪費(fèi)在如何將復(fù)雜的代碼分支結(jié)構(gòu)編織在一起之上。
雖然想要直截了當(dāng)?shù)貙懗鎏娲桨福紫任业脧?qiáng)調(diào)這句話:
凡事中庸而行,尤其是中庸本身
if 語(yǔ)句通常會(huì)讓代碼更加復(fù)雜,但這不代表我們要完全拋棄if語(yǔ)句。我曾經(jīng)看到過(guò)一些非常糟糕的代碼,只是為了消除所有的if語(yǔ)句而刻意避開(kāi)if語(yǔ)句。我們想要繞開(kāi)這個(gè)誤區(qū), 下面我給出的每種模式,都會(huì)給出使用范圍。
單獨(dú)的if語(yǔ)句如果不復(fù)制到其他地方,也許是不錯(cuò)的句子。在復(fù)制if語(yǔ)句時(shí),我們會(huì)希望預(yù)知危險(xiǎn)的第六感起效。
在代碼庫(kù)之外,在與危險(xiǎn)的外部世界交流時(shí),我們會(huì)想要驗(yàn)證incoming response,并根據(jù)其作出相應(yīng)的修改。但在自己的代碼庫(kù)中,由于有可靠的gatekeeper把關(guān),我覺(jué)得這是個(gè)很好的機(jī)會(huì),我們可以嘗試使用簡(jiǎn)單、更為豐富與強(qiáng)大的替代方案來(lái)實(shí)現(xiàn)。
背景: 有方法在修改行為時(shí)使用了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 } } }
問(wèn)題: 在看到這段代碼時(shí),實(shí)際上你是將兩個(gè)方法捆綁到一起,布爾參數(shù)的出現(xiàn)讓你有機(jī)會(huì)在代碼中定義一個(gè)概念。
適用范圍: 通常看到這種情況,如果在編譯時(shí)我們可以算出代碼要采用哪種路徑,就可以放心使用這種模式。
解決方案: 將這個(gè)方法拆分成兩個(gè)新的方法,然后if就不見(jiàn)了。
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 } }
背景: 根據(jù)類型switch時(shí)
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; } }
問(wèn)題: 在添加新的類型時(shí),我們必須要記得更新switch語(yǔ)句,此外隨著不同bird的概念添加進(jìn)來(lái),bird類的凝聚力越來(lái)越糟。
適用范圍:根據(jù)類型做單次切換是可行的,如果switch太多,在添加新類型時(shí)如果忘記更新現(xiàn)有隱藏類型中的所有switch,就會(huì)導(dǎo)致bug出現(xiàn),8thlight博客關(guān)于這種情況有詳細(xì)描述。
解決方案: 使用多態(tài),添加新類型時(shí)大家都不會(huì)忘記添加相關(guān)行為。 注意:上例為了簡(jiǎn)潔只寫了一個(gè)方法,但在有多個(gè)switch時(shí)更有用。
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(); } }
背景: 在計(jì)算布爾表達(dá)式時(shí),包含if語(yǔ)句樹
Java代碼
public boolean horrible(boolean foo, boolean bar, boolean baz) { if (foo) { if (bar) { return true; } } if (baz) { return true; } else { return false; } }
問(wèn)題: 這種代碼會(huì)導(dǎo)致開(kāi)發(fā)者必須用大腦來(lái)模擬計(jì)算機(jī)對(duì)方法的處理。
適用范圍:很少有不適用的情況,像這樣的代碼可以合成一行,或者拆成不同的部分。
解決方案: 將if語(yǔ)句樹合成單個(gè)表達(dá)式。
Java代碼
public boolean horrible(boolean foo, boolean bar, boolean baz) { return foo && bar || baz; }
背景:在調(diào)用一些其他代碼時(shí),無(wú)法確保路徑是成功的。
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; } } }
問(wèn)題: 這類if語(yǔ)句增加了處理同一個(gè)對(duì)象或者數(shù)據(jù)結(jié)構(gòu)的時(shí)間,其中包含隱藏耦合——null的情況。其它對(duì)象可能會(huì)返回其他代表沒(méi)有結(jié)果的magic value。
適用范圍:最好將這類if語(yǔ)句放在一個(gè)地方,由于不會(huì)重復(fù),我們就能將為空對(duì)象的magic value刪除。
解決方案:針對(duì)被調(diào)用代碼,給出應(yīng)對(duì)策略。Ruby的Hash#fetch就是很好的案例,Java也用到了類似的方法。這種模式也可以用在刪除例外情況時(shí)。
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"); } }
希望這些模式對(duì)你現(xiàn)在處理的問(wèn)題有幫助。我在重構(gòu)代碼增進(jìn)理解時(shí),發(fā)現(xiàn)這些方法都很有用。要記得并非所有if語(yǔ)句都是魔鬼,不過(guò)現(xiàn)代編程語(yǔ)言還有很多功能值得我們探索并使用。
本文來(lái)源:
本站文章除注明轉(zhuǎn)載外,均為本站原創(chuàng)或翻譯。歡迎任何形式的轉(zhuǎn)載,但請(qǐng)務(wù)必注明出處、不得修改原文相關(guān)鏈接,如果存在內(nèi)容上的異議請(qǐng)郵件反饋至chenjj@fc6vip.cn