? ? ? 姓名:王明騫? ? 學(xué)號:16050510061
轉(zhuǎn)載自:http://mp.weixin.qq.com/s/46r5mC2JDorB263lHhRfTw? ? 有刪改
[嵌牛導(dǎo)讀]
? ? ? ? 當(dāng)媽媽不注意的時候翁巍,偷偷地吃糖果零食胀茵,然后導(dǎo)致有了蛀牙。同樣的牛哺,我們都違背過一些編程的基本規(guī)則,并且都會堅定地表示這種行為是不可取的莉御,但我們就是偷偷愛著這些不良的編程習(xí)慣寝优。
[嵌牛鼻子]
? ? ? ? 使用goto,成功避開文檔叙赚,一行寫太多代碼,不聲明類型僚饭,搖擺不定的代碼震叮,編寫你自己的數(shù)據(jù)結(jié)構(gòu),在中間使用循環(huán)鳍鸵,使用短變量名苇瓣,重新定義運算符和函數(shù)
[嵌牛提問]
? ? ? ? 程序員如何擺脫不良習(xí)慣?如何做一個高效的程序員权纤?
[嵌牛導(dǎo)讀]
? ? ? ? 我們對所謂的編程規(guī)則嗤之以鼻钓简,輸出的代碼也很糟糕——但我們依然活著。編程上帝沒有下閃電劈死我們汹想,我們的電腦也沒有爆炸外邓。事實上,只要我們能編譯和發(fā)布代碼古掏,客戶似乎就很滿意了损话。
? ? ? ? 這是因為糟糕的編程不像安裝電路或者摸老虎屁股那樣有直接的危害性。大多數(shù)時間里它也是可以工作的槽唾。規(guī)則通常是作為一種指導(dǎo)或格式上的建議丧枪,并沒有硬性規(guī)定一定要遵守,也不會導(dǎo)致代碼馬上死掉庞萍。當(dāng)然拧烦,你的代碼可能會被人恥笑,甚至可能大家公開嘲笑你钝计,不過恋博,這種挑戰(zhàn)慣例的行為可以讓人增加一點顛覆傳統(tǒng)的快感,哪怕是在不經(jīng)意間私恬。
? ? ? ? 為了不讓問題變得更加復(fù)雜债沮,有時候違反規(guī)則反而更好。(一般人我不告訴他1久)出來的代碼會更干凈疫衩,甚至可能會更快和更簡單。規(guī)則通常顯得太過于寬泛荣德,有技巧的程序員可以通過打破這些規(guī)則來提高代碼闷煤。不要告訴你的老板,這對你的編碼生涯會很有意義命爬。
? ? ? ? 下面這9個編碼習(xí)慣曹傀,雖然在編程規(guī)則中是被駁斥的,但我們很多人就是會不由自主地使用它們饲宛。
使用goto
? ? ? ? 關(guān)于禁止使用goto可以追溯到許多結(jié)構(gòu)化編程工具還未面世的時代皆愉。如果程序員想要創(chuàng)建一個循環(huán)或跳到另一段程序中,那么他們需要輸入goto后再跟一個行號艇抠。過了幾年之后幕庐,編譯器團隊讓程序員使用字符串標簽取代行號。這在當(dāng)時被認為是一個熱門的新功能家淤。
? ? ? ? 有的人認為這會導(dǎo)致“意大利面條式代碼”异剥。代碼會變得不可讀,并且很難理解代碼的執(zhí)行路徑絮重。線程混亂冤寿,纏纏綿綿到天涯歹苦。Edsger Dijkstra就三令五申地表示應(yīng)該禁止這個命令,他有一份詼諧的手稿督怜,題目為《Goto語句害人不淺》殴瘦。
? ? ? ? 但絕對的分支是沒有問題的。這就讓人糾結(jié)了号杠。通常蚪腋,巧妙的 break 語句和return 語句可提供一個非常干凈的關(guān)于代碼在那個時候執(zhí)行什么的聲明。有時候姨蟋,添加 goto 到case語句會比更恰當(dāng)?shù)亩嗉壡短椎膇f-then-else語句塊更易于理解屉凯。
? ? ? ? 也有反例。在蘋果的SSL堆棧中的“goto fail”安全漏洞就是最好的例子之一眼溶。但是悠砚,如果我們能夠仔細避免case語句和循環(huán)的一些尷尬問題,那么我們就可以嵌入良好的絕對轉(zhuǎn)移堂飞,使閱讀代碼的人更容易明白這是怎么回事哩簿。我們可以插入break和return 語句,讓每一個人感覺更清潔和更愉快——可能得除了goto的敵視者酝静。
成功避開文檔
? ? ? ? 我的一個朋友有一個非常精明的老板节榜,這位老板雖然從來沒有寫過任何代碼,但卻秉持著每一個功能都必須包含在文檔中的理念别智。哪個程序員不提供注釋宗苍,那么他就會受到懲罰。所以薄榛,我的朋友在他的編輯器中聯(lián)入了一個有點像人工智能的玩意兒讳窟,于是乎,他的每一個功能就都有幾行“文檔”了敞恋。因為這位精明的老板還不夠聰明到能理解這些注釋其實啥意思也沒有丽啡,所以我的朋友逃過一劫。他的代碼常常被作為正式文檔硬猫。我想补箍,他應(yīng)該快要升職了!
? ? ? ? 許多函數(shù)方法啸蜜,甚至一些類或多或少都能自文檔化坑雅。冠以insertReservation或cancelReservation或 deleteAll 等名稱的函數(shù)并不需要多此一舉來解釋它們的作用。為函數(shù)取一個正確的名字往往就足夠了衬横。事實上裹粤,這比寫一段長長的注釋要好,因為函數(shù)名可以出現(xiàn)在代碼中的其他地方蜂林。而文檔只能默默地呆在某個角落遥诉。自文檔化的函數(shù)名可以改進它們出現(xiàn)的每個文件拇泣。
? ? ? ? 在有些情況下,寫文檔甚至?xí)?dǎo)致情況變糟矮锈。例如挫酿,當(dāng)代碼瞬息萬變,團隊像瘋了似的重構(gòu)的時候愕难,文檔會產(chǎn)生分歧。代碼是這樣寫的惫霸,但文檔解釋的還是四五個版本以前的情況猫缭。這類“過時”的文檔通常位于代碼頂部,有的人會在這里對代碼應(yīng)該發(fā)生什么作一個美好總結(jié)壹店。因此猜丹,盡管重構(gòu)團隊已經(jīng)仔細修改了相關(guān)的注釋,但還是會遺漏文件頂部的這段“美好總結(jié)”硅卢。
? ? ? ? 當(dāng)代碼和文本出現(xiàn)分歧的時候射窒,注釋就變得毫無價值,甚至?xí)a(chǎn)生誤導(dǎo)将塑。在這樣的情況下脉顿,良好的自文檔化的代碼顯然勝出了。
一行寫太多代碼
? ? ? 老板突然發(fā)神經(jīng)地給團隊發(fā)了一封討厭的郵件:為了執(zhí)行非常嚴格的風(fēng)格規(guī)定点寥,我們大家都必須重寫我們的代碼艾疟。最神奇的要求是:每個行為或步驟或子句必須各自成行。你不能使用點語法連續(xù)調(diào)用函數(shù)敢辩。在一個分支語句中蔽莱,你不能有兩個及以上返回布爾值的子句。如果要定義變量戚长,那么另起一行盗冷。如果你正在做一個復(fù)雜的計算,那么不要使用括號同廉。每個片段也自成一行仪糖。
? ? ? ? 他認為他的這個法令將能使調(diào)試變得更加容易。就像你單步調(diào)試代碼一樣迫肖,調(diào)試器會一個動作一個動作地前進乓诽。這樣就不會卡在某一行。而且更容易執(zhí)行咒程。但是這樣一來鸠天,鍵盤上的回車鍵煩不勝煩,因為我需要不斷地插入行帐姻。而且我敢肯定稠集,老板因此還可以到處吹噓他的團隊能寫多少行代碼奶段。
? ? ? ? 唉,有時在同一行中聲明一堆變量反而更容易剥纷;有時把所有的布爾子句放在一起反而更簡單——一切都能變得更加緊湊痹籍。那也意味著,我們可以在屏幕上看到更多的邏輯而無需滾動鼠標晦鞋。更易于閱讀就意味著理解起來更快蹲缠。這才是簡單的精粹。
不聲明類型
? ? ? 那些熱愛類型化語言的人認為悠垛,如果為每個變量添加明確的數(shù)據(jù)類型聲明线定,就可以寫出更好的、沒有錯誤的代碼确买〗锛ィ花一點時間來拼寫類型,能幫助編譯器在代碼開始運行之前標志愚蠢的錯誤湾趾“派蹋可能會讓人覺得痛苦,但很有幫助搀缠。這是編程中停止bug的一種有備無患的方法铛楣。
? ? ? ? 但是時代變了。許多較新的編譯器完全可以智能地通過查看代碼來推斷類型艺普。它們會向后和向前瀏覽代碼蛉艾,直到可以肯定這個變量是string 還是int,抑或其他衷敌。如果這些被查看的類型不成隊列勿侯,那么錯誤標志就會點亮。因此再也不需要我們輸入變量的類型了缴罗。
? ? ? ? 這意味著我們現(xiàn)在可以在代碼中省略掉一些最簡單的聲明助琐。代碼更清潔,而且閱讀代碼的人也猜得出for循環(huán)中命名為 i 的變量表示一個整數(shù)型面氓。
搖擺不定的代碼
? ? ? ? 有的程序員在代碼上特別優(yōu)柔寡斷兵钮,猶豫不決。先是一開始將值存儲為字符串舌界,然后又解析成整數(shù)掘譬。接著又轉(zhuǎn)換回字符串。這是非常低效的呻拌,你甚至可以感覺到CPU在咆哮這種浪費負載的行為葱轩。聰明的程序員之所以能快速地編碼,是因為他們事先會設(shè)計架構(gòu),以盡量減少轉(zhuǎn)換靴拱。他們的代碼能更快地運行是因為他們有一個良好的規(guī)劃垃喊。
? ? ? ? 但是,不管你信不信袜炕,這種搖擺不定的代碼有時候也是有意義的本谜。比如說,你有一個非常棒的庫偎窘,在它專有的黑盒子里能做無數(shù)智能的事情乌助。如果庫需要字符串的數(shù)據(jù),那么你就給它字符串陌知,即使你剛將這個數(shù)據(jù)轉(zhuǎn)換成為整數(shù)型他托。
? ? ? ? 當(dāng)然,你可以重寫所有的代碼纵诞,以盡量減少轉(zhuǎn)換,但是這需要時間培遵。而且浙芙,有時候讓代碼稍微多花點額外時間來運行也未嘗不可,因為重寫代碼需要耗費我們更多的時間籽腕。有時嗡呼,背負這樣的技術(shù)債務(wù)比一開始就正確構(gòu)建的成本要更低。
? ? ? 有的時候皇耗,庫不是專有的代碼南窗,但那些你以前全部自己寫的代碼是你獨有的。有的時候郎楼,再次轉(zhuǎn)換數(shù)據(jù)比重寫庫中的所有代碼要快得多万伤。所以,就讓它這樣吧呜袁,就讓代碼搖擺吧敌买。
編寫你自己的數(shù)據(jù)結(jié)構(gòu)
? ? ? ? 有一個標準規(guī)則是,程序員在完成數(shù)據(jù)結(jié)構(gòu)課程的第二年阶界,不應(yīng)該寫用于存儲數(shù)據(jù)的代碼虹钮。基本上我們需要的所有的數(shù)據(jù)結(jié)構(gòu)膘融,已經(jīng)有人寫好了芙粱,而且其代碼已歷經(jīng)多年的測試和再測試。它和語言捆綁在一起氧映,而且常常是免費的春畔。你的代碼只能造就bug。
? ? ? ? 但有時你會發(fā)現(xiàn)數(shù)據(jù)結(jié)構(gòu)庫有點慢。有時它們會迫使我們使用標準的拐迁,但于我們的代碼卻是錯誤的結(jié)構(gòu)蹭劈。有時庫會把我們推向在使用結(jié)構(gòu)之前重新配置數(shù)據(jù)的地步。有時庫會包含一些所謂有備無患的保護功能线召,如線程鎖铺韧,但其實我們的代碼并不需要。
? ? ? ? 如果遇到這種情況缓淹,那么就應(yīng)該著手寫我們自己的數(shù)據(jù)結(jié)構(gòu)哈打。這或許能讓你做得更快,做得更多讯壶。而且代碼會變得更清潔料仗,因為我們不會包括那些多余的用于格式化數(shù)據(jù)來完成一些功能的代碼。
在中間打破循環(huán)
? ? ? ? 有一個規(guī)則制定小組宣稱伏蚊,每個循環(huán)都應(yīng)該有一個“常量”立轧,也就是說當(dāng)這個邏輯語句為true的時候,循環(huán)一直執(zhí)行躏吊。當(dāng)常量一定不會是true的時候氛改,循環(huán)才會結(jié)束。這是考慮復(fù)雜循環(huán)的好方法比伏,但它會導(dǎo)致愚蠢的禁令——例如禁止我們在循環(huán)中間使用return 和break 語句胜卤。這一條也包含在禁止goto語句的規(guī)則中。
? ? ? 這個理論是好的赁项,但它通常會導(dǎo)致更復(fù)雜的代碼葛躏。請看下面這個簡單的案例,遍歷數(shù)組悠菜,將找到的元素傳遞給test函數(shù)舰攒,并將該元素返回:
while (i<a.length){
? ...
? if (test(a[i]) then return a[i];
? ...
}
“循環(huán)常量”愛好者會要求我們增加一個布爾變量,命名為notFound悔醋,然后這樣使用:
while ((notFound) && (i<a.length){
...
if (test(a[i])) then notFound=false;
...
}
如果這個布爾值能夠合理地命名芒率,那么這就是一段很棒的自文檔化的代碼,更易于大家理解篙顺。但這也增加了復(fù)雜性偶芍。這意味著你需要分配另一個局部變量,并堵塞寄存器德玫,因為編譯器也許還不能足夠智能到解決這個問題匪蟀。有時候,一個goto 語句或一個跳轉(zhuǎn)會更干凈利索宰僧。
? ? ? ? 使用短變量名(i和x和and也是有意義的)
? ? ? ? Edgar Allan Poe這位詩人和小說家曾經(jīng)說過材彪,在一個故事中的每一個詞都應(yīng)該是有內(nèi)涵的。編碼規(guī)則也強調(diào)如此。變量名應(yīng)該說明這個變量的所作所為段化。那些使用駝峰式大小寫的方法來寫變量名嘁捷,以表達關(guān)于變量細節(jié)的Java程序員深以為然,于是一個又一個瘋狂長度的變量名出爐了显熏。有些程序員寫的變量名雄嚣,會組合五六個甚至更多的詞語。
? ? ? ? 但有的時候喘蟆,使用單個字母作為變量名反而會更方便缓升。有時在循環(huán)迭代中只使用i或j會更簡單。有時使用字母a代表array 蕴轨,l代表list會更便捷港谊,即使是字母l和數(shù)字1看上去很難辨別。
? ? ? 正如這篇文章前面鼓勵的是自文檔化的代碼橙弱,而非長長的注釋歧寺。在上述情況下,單個字母的變量名也是自文檔化的棘脐。字母 i 是通用的迭代器斜筐。只要是程序員立刻就會懂。
重新定義運算符和函數(shù)
? ? ? 一些最有趣的編程語言允許你去做一些特別詭異的事情荆残,例如重新定義元素的值奴艾,就如同常量一般净当。例如Python内斯,你可以輸入TRUE=FALSE(在Version2.7及之前的版本)。這并不會產(chǎn)生某種邏輯崩潰像啼,或?qū)е掠钪娼K結(jié)——僅僅只是互換了TRUE和FALSE的含義俘闯。你也可以在C預(yù)處理器和一些其他語言中玩玩類似于這樣的危險游戲。還有一些語言允許你重新定義運算符忽冻,如加號真朗。
? ? ? ? 當(dāng)然這是延伸了,不過有一個觀點是僧诚,在一個大的代碼塊內(nèi)遮婶,當(dāng)重新定義一個或多個所謂的常量時,速度會更快湖笨。有時老板會要求代碼做一些截然不同的事情旗扑。當(dāng)然,你可以修改代碼的每個事件慈省,或者臀防,你可以重新定義。這讓你看上去像一個天才。不必重寫一個龐大的庫袱衷,只需翻轉(zhuǎn)一下捎废,就可以做相反的事情了。
? ? ? ? 這9個習(xí)慣就都在這兒了致燥。千萬不要輕易嘗試登疗,不管它看上去有多牛掰。太危險了——真的篡悟,這是實話谜叹。