開發者常見的十種不良編程習慣
譯文【51CTO.com快譯】常言道:嘴上說不要,習慣卻很誠實。朋友,您是否捫心自問過:有過多少次違背了編程的基本規范?或許您根本就沒有意識到自己會常犯的那些不良編程習慣。實際上,我們在交付已編譯好的代碼后,客戶是不會在使用中馬上發現到程序的缺陷。
通常,編程規則只會規定程序員在編寫代碼時,需要遵守的準則、以及建議采用的風格,而不是那些細枝末節需要遵循的、一成不變的指令。因此,就算您偶有“犯規”也并不可怕。可怕的是不去認真總結,甚至置若罔聞。下面,我將和您討論十種常見的不良編程習慣。
不良的編程習慣1:直接復制操作
請不要直接將他人的代碼整塊地復制、粘貼到自己的程序中,特別是那些已經標記了版權信息的專有代碼。請最好在理解的基礎上,動手鍵入自己的版本。畢竟,您的老板或客戶并不是花錢來請你專門進行復制、粘貼的。
當然,從代碼的可靠性來說,我們通常應當從信譽良好的來源,獲取那些經過原創作者深思熟慮、以及反復測試的源代碼。為了避免重復造輪子,許多原始創作者會在各種在線編程論壇上共享、并開放自己的源代碼。不過,有時候他們可能會帶有許可證(如BSD或MIT)、以及使用范圍的限制。
另外,有些程序往往是針對某個特定的棘手問題所開發的。其中難免會存在著一些未被發現的錯誤、或是對具體情況的假設與限制條件。如果您只是將其簡單地添加到自己的代碼段中的話,很有可能會出現空指針、或是死循環之類的潛在錯誤。而由于原始代碼并未預見到您的使用環境,因此可能會出現“水土不服”,甚至是實現效果并不理想的情況。
不良的編程習慣2:非功能性的代碼
在過去的十年間,功能性范式有了長足進步。有多項研究證明:使用嵌套函數調用程序來構建程序的方法,與舊式的變量和循環模式相比,程序代碼本身會更加安全,錯誤也會更少。因此,資深程序員往往會通過審查代碼、以及推拉請求的方式,來發現那些非功能性的代碼,進而讓程序的結構更為合理且協調。
請不要妄想一次性設計出全面的架構。畢竟,精心的分層設計、復雜的界面導航、以及用于構建的配套代碼,都需要事先向程序員提供充裕的時間、以及昂貴的費用。因此,為了確保所有必要的數據能夠正確地被定義,并能夠通過準確的途徑進行傳遞,進而讓整個系統能夠以高效的方式運行起來,我們應當盡量保持代碼的簡潔與直觀,不要遺留各種冗長的非功能性代碼。
不良的編程習慣3:非標準的間距
眾所周知,除了諸如Python之類少數編程語言使用空格間距來標識代碼塊之外,大多數程序中的空格一般都不會影響軟件的行為效果、以及整體性能。盡管如此,我們依然需要抱著“強迫癥”的態度,苛求自己不要寫出“非標的代碼(Non Standard Code)”,特別是在等號的兩側不要留下空格,以至于違反了ESLint space-infix-ops規則。
有時候,過多的空格會導致數據庫的過載,甚至是由于空指針導致了程序運行的崩潰。因此,在某些要求嚴格的軟件開發企業中,他們會專門成立標準委員會,以規范各種空格、或制表符在代碼、以及頁面中所允許出現的位置。
在實際編程過程中,開發人員往往更專注于程序的功能與效果,而忽略了間距與空格之類的格式問題。不過,值得慶幸的是:目前有好幾種工具可以幫助您自動化地規范代碼的格式,并能遵守各種預先定義的插入規則。
不良的編程習慣4:goto的使用
人們使用goto的歷史可以追溯到那些早于結構化編程工具的時代。當時,如果程序員們想創建一個循環,或是跳轉到另一個例程,就需要鍵入goto、且后面跟上行號。若干年后,編譯器出現了,它允許程序員使用字符串標簽,而不再是行號。當時,此類功能曾經被認為是一種熱門技能。
如今,有越來越多的人將此類編程習慣所產生的代碼稱為“意面式代碼(spaghetti code)”。此類混為一團的線程糾纏在一起,他人完全無法讀懂,而且其本身很可能會陷入遵循執行的路徑。結構程序設計之父Edsger Dijkstra曾經針對此現象發表了《Goto聲明顯然有害(Goto Statement Considered Harmful)》的文章。
作為解決方案,您可以巧妙地使用break或return,來清晰地聲明代碼執行到此處的后繼行為。當然,我們有時候也可以把goto添加到case語句中,將產生比級聯“if-then-else”塊結構,更為正確的列表,以及更容易理解的內容。
作為一個反例,您也許聽說過蘋果SSL堆棧中的“goto fail”安全漏洞。如果我們能夠謹慎地避免case語句與循環相關的棘手問題,只插入break或return之類有效的絕對跳轉,那么就會讓整個問題容易處理得多。
不良的編程習慣5:未聲明的類型
有經驗的程序員都知道:只有為每個變量的數據類型都添加清晰的聲明,我們才能編寫出更優秀,bug更少的代碼。我們只有提前完善了類型的聲明,編譯器在執行程序代碼時,才不會碰到某些“愚蠢”的錯誤。這種編程習慣的養成雖然可能很痛苦,但是它絕對會讓您受益。
當然,隨著技術的發展,如今許多新式的編譯器已經足夠聰明,能夠通過查看代碼的上下文,來推斷變量是否屬于string、int或其他類型。如果編譯器實在無法推斷出正確的類型,它也會拋出相應的錯誤。
不良的編程習慣6:yo-yo代碼
“yo-yo代碼”是指:作為字符串存儲的數值被解析為整數,接著又被轉換回了字符串。顯然,這是一種非常低效的方式。它額外地耗費了大量的CPU資源。因此,經驗豐富的碼農會通過合理的程序結構,來大幅減少轉換所需的工作量,進而提升代碼的運行效率。
如今我們時常聽到業界有人談論償還“技術債”的說法。它在此處可以被理解為:程序員通過重寫所有現有的代碼,清除yo-yo代碼,以最大限度減少數值類型轉換的成本與時間。
不良的編程習慣7:編寫自己的數據結構
一些初出茅廬的程序員往往喜歡自行編寫某種用于存儲數據的代碼。而這些數據結構難免存在著他們考慮不周的潛在漏洞。其實他們應該明白:作為一種成熟的編程語言,各種常見的數據結構已有前人提前準備好了。而且這些程序代碼往往與語言捆綁在一起,經過了多年的測試與檢驗,開發者可以放心大膽地免費使用。
當然,有時候現成的數據結構庫為了標準化的需要,而在存放之前迫使我們重新配置輸入的數據。因此,我們可以適當地針對線程鎖等功能進行代碼級別的調整,去除不必要的、數據格式化之類的代碼,以使得整體結構更加簡潔。
不良的編程習慣8:老式循環
很久以前,C語言的締造者希望將所有的抽象可能性都封裝到一個簡單的構造之中,這就導致了每次調用都需要循環地進行各項操作,并需要告知操作的完成。顯然,這會讓程序的可讀性變得很差。如今,人們更趨向具備功能性的范式(paradigm),直接將功能函數應用到列表中,將計算模板映射給某些數據,而不再使用循環。
在只有一個整潔的函數和一個數組的時候,使用不帶循環的方法會讓程序變得更加易讀且簡潔。但是在需要首次找到匹配項就立即停止搜索的場景下,老式的循環仍然會讓程序既簡單又高效。
當然,映射函數更適合您對數據執行多項操作。例如:您想先取絕對值,然后取每個數字的平方根。那么最快的方法就是:先映射第一個函數,然后映射第二個,接著將數據循環兩次。
不良的編程習慣9:從循環中跳出
有些程序員在編程的時候,喜歡在每個循環中都使用一個變量,并且在該變量為真(true)的時候,就結束循環。雖然這是處置復雜循環的一種好方法,但是它會在某種程度上禁止我們在循環中使用return或break。
不良的編程習慣10:重新定義運算符和函數
對于某些初級程序員而言,由于知識與經驗的不足,他們可能會重新定義運算符和函數。例如,在Python 2.7、及其之前的版本中,您可以指定TRUE=FALSE。這將導致的結果是:它會在您的代碼中將TRUE和FALSE的含義進行互換。同時,在C語言預處理器、以及某些其他語言中,您也可以去重新定義加號之類的運算符。
當然,此類重新定義也并非百無一用。面對巨大的既有庫,您可以在無需重新改寫代碼的情況下,臨時借用一下該“反轉”功能,來簡化大段的代碼。
綜上所述,我們在此羅列的所謂開發者常犯的十項惡習,實際上并非一成不變的。關鍵就在于您是否能夠在恰當的場景下,使用恰當的編程方式。
原文標題:10 bad programming habits we secretly love,作者:Peter Wayner
【51CTO譯稿,合作站點轉載請注明原文譯者和出處為51CTO.com】