你離頂尖的程序員還差十步

你離頂尖的程序員還差十步

我們都有過這樣的經歷:在媽媽不注意的時候偷吃一塊餅乾;聚餐時過量飲酒;把車停在超時停車的停車位上——我們甚至在死亡邊緣瘋狂試探。是的,我們違反了許多編程的基本規則,那些大家都認為是不好的規則,而我們卻偷偷地使用著。我們對好的編程規則嗤之以鼻,輸出的代碼也一塌糊塗——但我們做出來了。沒有來自編程之神的五雷轟頂,我們的桌面也沒有爆炸。事實上,只要我們的代碼可以編譯並交付,客戶似乎就很滿意了。這是因為糟糕的編程不像安裝電路或者摸老虎屁股那樣有直接的危害性。大多數情況下,它也是可以運行的。規則通常是作為一種指導或格式上的建議,並沒有硬性規定一定要遵守,也不會導致代碼出現問題。當然,你的代碼可能會被人恥笑,甚至可能大家公開嘲笑你。不過,這種挑戰慣例的行為可以讓人增加一點顛覆傳統的快感,哪怕是在不經意間。讓事情變得更復雜的是,有時候違反規則反而更好。輸出的代碼會更乾淨,甚至可能會更快更簡單。規則通常顯得太過於寬泛,有技巧的程序員可以通過打破這些規則來優化代碼。不要告訴你的老闆,這對你的編碼生涯會很有意義。下面這十條編碼習慣常常是被駁斥的,但我們很多人就是會不由自主地使用它們,覺得好用且實用。

編程壞習慣一:拷貝

在學校裡,拷貝是不對的。而在工作中,規則不是很明確。當然有一些代碼塊不應該被竊取。如果它來自私有代碼,不要將它拷貝到你的堆棧中,特別是標記了版權信息的。請編寫自己的版本,這是有報酬的。更棘手的問題出現在原創者想要分享的時候。可能是在一個在線編程論壇上;也可能是具有許可證的開放源代碼(BSD、MIT),它允許竊取其中一到三個函數。你無需承擔法律責任。你的工作是解決問題,而不是重新發明輪子。大多數情況下,拷貝的優點是不可抗拒的,而缺點可以稍加限制。從一個可靠來源處獲得的代碼已經至少經過一輪思考與實踐。最初的作者尋找解決方案並找到新思路,給出了循環不變量和數據流。拷貝的棘手問題是,是否存在一些未發現的bug,或者關於角色或底層數據的一些不同假設。也許你的代碼混合了空指針,而原始代碼卻從未檢查出來。如果你能解決這些問題,就好比你的老闆能從兩個程序員那裡得到輸入一樣。這是結對編程,沒有華麗的鋪陳。

編程壞習慣二:非功能性代碼

在過去的十餘年裡,功能範式一直在提升。研究表明,在調用嵌套函數的基礎上構建程序,代碼會比老式的變量和循環更安全、更少bug,所有這些都足以使程序員滿意。狂熱者義憤填膺地譴責代碼審查和拉取請求中的非功能性請求。這可能是真正的優勢。但有時你只需要複製粘貼。精心設計和優雅規劃的代碼不僅需要時間來想象,還需要時間來構建和導航。所有這些層都增加了複雜性,而複雜性就是金錢。寫出漂亮函數代碼的開發人員需要提前規劃,並確保所有數據都沿著正確的路徑傳遞。有時候改變一個變量更容易,這也許可以寫個評論來解釋一下。即使在評論中向未來幾代人註上一長串真誠的道歉,也比以正確的方式來重構整個系統要快得多。

你離頂尖的程序員還差十步


編程壞習慣三:不標準的間距

軟件中的大多數空格對程序的執行沒有影響。除了像Python等少數使用空格表示代碼塊的語言外,大多數空格對程序的行為沒有影響。儘管如此,仍有一些執迷於此的程序員,他們計空格數並堅持認為這很重要。曾有人義正言辭的向我老闆控訴我在寫“非標準代碼”,且他一眼就看出來了。我的錯咯?沒有在等號兩邊都加上空格,違反了ESLint space-infix-ops規則。有時你必須考慮比空間位置更深層次的東西。或許你擔心數據庫超載,又或許你擔心一個空指針會使你的代碼崩潰。幾乎代碼的任一部分都比空格更重要,即使挑剔專橫的標準委員會已經制定了諸多關於空格或製表符位置的規則。令人驚奇的是,有幾個很好的工具可以自動地重新格式化代碼,以遵循任何規定的linting規則。我們不需要花時間去思考這個問題,如果它如此重要,我們可以通過運行工具來清理問題。

編程壞習慣四:使用goto

禁止使用goto可以追溯到許多結構化編程工具尚未面世的時代。如果程序員想創建一個循環或跳轉到另一個程序中,他們需要輸入GOTO再在後面加上一個行號。幾年後,編譯器團隊允許程序員使用字符串標籤而不是行號。這在當時被認為是一個熱門的新功能。有些人認為這會導致“意大利麵式的代碼”。代碼會變得不可讀,並且很難理解代碼的執行路徑。那是一團亂麻,永遠纏繞在一起。Edsger Dijkstra用一篇名為“Goto語言害人不淺”的滑稽手稿建議禁止goto命令。但絕對分支是沒有問題的。這就讓人糾結了。通常,巧妙的break或return將提供一個非常清晰的聲明,說明代碼在該位置正在做什麼。有時候,將goto添加到case語句中會更容易,而不是一組結構更合理的層疊if-then-else塊。也有反例,蘋果SSL協議棧中的“goto fail”安全漏洞就是最好的例子之一。但是,如果我們小心避免case語句和循環的一些棘手問題,我們可以插入良好的絕對跳轉,讓讀者更容易理解。我們可以插入一個break或return,這對每個人來說都更乾淨簡明——可能除了那些goto厭惡者。

那些熱愛類型化語言的人認為,如果為每個變量添加明確的數據類型聲明,就可以寫出更好、沒有bug的代碼。在代碼運行前,花一段時間來拼寫出類型,可以幫助編譯器標記出愚蠢的錯誤。這可能不容易做到,但它是有幫助的。這是編程中阻止bug的一種有備無患的方法。但是時代變了。許多較新的編譯器足夠智能,可以通過查看代碼來推斷類型。它們會前後反覆的查看代碼,直到確定變量是string,int或其他類型。如果這些推斷的類型不成隊列,那麼編譯器就會拋出一個錯誤標誌。因此就不再需要我們輸入變量了。這意味著現在可以通過省略一些最簡單的聲明來簡化代碼。代碼變得更簡潔了,而且讀者通常能夠猜到在for循環中名為i的變量是一個整數。

編程壞習慣六:溜溜球代碼

程序員喜歡稱之為“溜溜球代碼”。一開始先將值存儲為字符串,然後又解析成整數,接著又轉換回字符串。這是非常低效的。你幾乎可以感覺到CPU在所有額外負載下的掙扎。聰明的程序員之所以能快速的編碼,是因為他們事先會設計架構,以儘量減少轉換。因為他們的良好規劃而使得代碼更快的運行。但是不管你信不信,有時候這種溜溜球代碼是有道理的。比如你有一個很棒的庫,在其專有的黑盒子裡能做無數智能的事情。又比如,老闆會開一張七位數的支票,賦予黑盒子所有傑出功能。如果庫需要字符串形式的數據,那你就給它字符串,即使你剛將其轉換為整數。當然,你也可以重寫所有代碼以儘量減少轉換,但這需要時間。有時,代碼多運行一分鐘、一小時、一天甚至一週都沒有問題,因為重寫代碼會花費更多的時間。有時候,積累技術債務比一開始就把它建好要便宜。有時,這個庫不是私有代碼,而是你很久以前自己編寫的代碼。有時,將數據轉換一次比重寫庫中的所有內容更快。所以,就繼續寫溜溜球代碼吧,沒關係的,我們都經歷過。

你離頂尖的程序員還差十步

>

編程壞習慣七:編寫自己的數據結構

其中一個標準規則是,程序員在大二時修完數據結構課程後,絕不應該編寫用於存儲數據的代碼。其他人已經編寫了我們需要的所有數據結構,他們的代碼經過了多年的測試和重新測試。它與語言捆綁在一起,而且可能是免費的。而你寫的代碼可能只有bug。但有的時候,你會發現數據結構庫有點慢。有時它們迫使我們進入一個可能是標準的但對我們的代碼來說是錯誤的結構。有時,在使用結構之前,庫會要求我們重新配置數據。有時庫包含一些所謂有備無患的保護功能,而我們的代碼不需要它們。如果遇到這種情況時,我們就該編寫自己的數據結構了。這可能會更快更好。有時它使我們的代碼更簡潔,因為我們沒有包含所有額外的代碼來精確地重新格式化數據。

編程壞習慣八:老式的循環

很久以前,有人創建了C語言,希望將所有抽象的可能性封裝在一個簡單的結構中。某些事情在開始時就要做,某些在每次循環時要做,還有一些方法可以告訴你什麼時候完成。當時,它似乎是一種捕捉無限可能性的完美語法。然後,現在一些現代的責罵者只看到其不便性。太繁瑣了。所有這些好的可能性也同樣可能是壞的。這使得閱讀和拓展變得更加困難。他們喜歡功能範型,沒有循環,只有應用於列表的函數,計算模板映射到一些數據。有時,無查找方式更簡潔,特別是只有一個整潔的函數和一個數組時。但有時老式的循環要簡單得多,因為它可以做更多的事情。例如,如果您可以在找到第一個匹配項時立即停止,那麼搜索它就會變得更簡單。此外,當需要對數據執行多個操作時,映射函數會鼓勵更草率的編碼。假設你想取絕對值然後取每個數的平方根。最快的解決方案是映射第一個函數,然後映射第二個函數,對數據進行兩次循環。

編程壞習慣九:中途跳出循環

規則制定小組宣稱,在代碼行中的某個地方,每個循環都應該有一個“常量”,即在整個循環中邏輯語句為真。當該常量不再為真時,循環結束。這是考慮複雜循環的一種好方法,但它會導致愚蠢的禁令——比如禁止我們在循環中間使用return或break。這也包含在禁止goto語句規則中。這個理論很好,但是它通常會導致更復雜的代碼。考慮這種簡單的情況,遍歷數組,將找到的元素傳給test函數,並將該元素返回:

while (i<a.length>

if (test(a[i]) then return a[i]

...

}

循環常量愛好者會要求我們添加另一個布爾變量,命名為notFound,然後這樣使用:

while ((notFound) && (i<a.length>

...

if (test(a[i])) then notFound=false;

...

}

如果這個布爾值命名正確,它就是一段很好的自文檔化代碼,更易於大家理解。但它也增加了複雜性。它還意味著分配另一個局部變量並阻塞寄存器,而編譯器可能不夠智能,無法修復這些問題。

有時,一個goto語句或一個跳轉會更乾淨利索。

編程壞習慣十:重新定義預算符和函數

一些最有趣的語言可以讓你做一些尤為曲折的事情,比如重新定義看起來應該是常量的元素的值。例如,Python允許輸入TRUE=FALSE,至少在2.7版之前是這樣的。這並沒有造成某種邏輯的崩潰和宇宙的終結;它只是交換了TRUE與FALSE的意義。你也可以用C預處理器和其他一些語言玩類似的危險遊戲。還有一些語言允許你重新定義運算符,比如加號。這是一種延伸,但是當要更快速的重新定義一個或多個所謂的常量時,其在代碼塊中意義非凡。有時候,老闆希望代碼做一些完全不同的事情。當然,你可以遍歷代碼並更改每個事件,或者重新定義。它會讓你看起來像個天才。不需要重寫一個龐大的庫,只需稍微翻轉一下,它就會執行相反的操作。也許在這裡劃清界限比較好。你不應該在家裡嘗試做這個,不管它有多聰明和有趣。這太危險了——真的......

小編分類整理了許多java進階學習材料和BAT面試題,需要資料的請小編就能領取2019年java進階學習資料和BAT面試題以及《Effective Java》(第3版)電子版書籍。

"/<a.length>

/<a.length>


分享到:


相關文章: