老碼農冒死揭開行業黑幕:如何編寫無法維護的程式碼(上篇)

Mr.Lin不想說話發表於2018-04-26

老碼農冒死揭開行業黑幕:如何編寫無法維護的程式碼(上篇)

簡介

永遠不要(把自己遇到的問題)歸因於(他人的)惡意,這恰恰說明了(你自己的)無能。— 拿破崙

為了造福大眾,在Java程式設計領域創造就業機會,兄弟我在此傳授大師們的祕籍。這些大師寫的程式碼極其難以維護,後繼者就是想對它做最簡單的修改都需要花上數年時間。而且,如果你能對照祕籍潛心修煉,你甚至可以給自己弄個鐵飯碗,因為除了你之外,沒人能維護你寫的程式碼。再而且,如果你能練就祕籍中的全部招式,那麼連你自己都無法維護你的程式碼了!

你不想練功過度走火入魔吧。那就不要讓你的程式碼一眼看去就完全無法維護,只要它實質上是那樣就行了。否則,你的程式碼就有被重寫或重構的風險!

總體原則

Quidquid latine dictum sit, altum sonatur.

想挫敗維護程式碼的程式設計師,你必須先明白他的思維方式。他接手了你的龐大程式,沒有時間把它全部讀一遍,更別說理解它了。他無非是想快速找到修改程式碼的位置、改程式碼、編譯,然後就能交差,並希望他的修改不會出現意外的副作用。

他檢視你的程式碼不過是管中窺豹,一次只能看到一小段而已。你要確保他永遠看不到全貌。要儘量讓他難以找到他想找的程式碼。但更重要的是,要讓他不能有把握忽略任何東西。

程式設計師都被程式設計慣例洗腦了,還為此自鳴得意。每一次你處心積慮地違背程式設計慣例,都會迫使他必須用放大鏡去仔細閱讀你的每一行程式碼。

你可能會覺得每個語言特性都可以用來讓程式碼難以維護,其實不然。你必須精心地誤用它們才行。

老碼農冒死揭開行業黑幕:如何編寫無法維護的程式碼(上篇)

其實做為一個java開發者,有一個學習的氛圍跟一個交流圈子特別重要,這是一個我的java交流群:475820025,邀請編號:寂靜。不管你是小白還是大牛都歡迎入駐,大家一起交流學習。

命名

“當我使用一個單詞的時候” Humpty Dumpty 曾經用一種輕蔑的口氣說, “它就是我想表達的意思,不多也不少。“

– Lewis Carroll — 《愛麗絲魔鏡之旅》

編寫無法維護程式碼的技巧的重中之重是變數和方法命名的藝術。如何命名是和編譯器無關的。這就讓你有巨大的自由度去利用它們迷惑維護程式碼的程式設計師。

妙用 寶寶起名大全

買本寶寶起名大全,你就永遠不缺變數名了。比如 Fred 就是個好名字,而且鍵盤輸入它也省事。如果你就想找一些容易輸入的變數名,可以試試 adsf 或者 aoeu之類。

單字母變數名

如果你給變數起名為a,b,c,用簡單的文字編輯器就沒法搜尋它們的引用。而且,沒人能猜到它們的含義。

創造性的拼寫錯誤

如果你必須使用描述性的變數和函式名,那就把它們都拼錯。還可以把某些函式和變數名拼錯,再把其他的拼對(例如 SetPintleOpening 和 SetPintalClosing) ,我們就能有效地將grep或IDE搜尋技術玩弄於股掌之上。這招超級管用。還可以混淆不同語言(比如colour — 英國英語,和 color — 美國英語)。

抽象

在命名函式和變數的時候,充分利用抽象單詞,例如 it, everything, data, handle, stuff, do, routine, perform 和數字,像這樣命名的好例子有 routineX48, PerformDataFunction, DoIt, HandleStuff還有 do_args_method。

首字母大寫的縮寫

用首字母大寫縮寫(比如GNU 代表 GNU’s Not Unix) 使程式碼簡潔難懂。真正的漢子(無論男女)從來不說明這種縮寫的含義,他們生下來就懂。

辭典大輪換

為了打破沉悶的程式設計氣氛,你可以用一本辭典來查詢儘量多的同義詞。例如 display, show, present。在註釋裡含糊其辭地暗示這些命名之間有細微的差別,其實根本沒有。不過,如果有兩個命名相似的函式真的有重大差別,那倒是一定要確保它們用相同的單詞來命名(例如,對於 “寫入檔案”, “在紙上書寫” 和 “螢幕顯示” 都用 print 來命名)。 在任何情況下都不要屈服於編寫明確的專案詞彙表這種無理要求。你可以辯解說,這種要求是一種不專業的行為,它違反了結構化設計的資訊隱藏原則。

首字母大寫

隨機地把單詞中間某個音節的首字母大寫。例如 ComputeReSult() 。

重用命名

在語言規則允許的地方,儘量把類、構造器、方法、成員變數、引數和區域性變數都命名成一樣。更高階的技巧是在{}塊中重用區域性變數。這樣做的目的是迫使維護程式碼的程式設計師認真檢查每個例項的作用域。特別是在Java程式碼中,可以把普通方法偽裝成構造器。

使用非英語字母

在命名中偷偷使用不易察覺的非英語字母,例如 typedef struct { int i; } ínt;

看上去沒啥不對是吧?嘿嘿嘿…這裡的第二個 ínt 的 í 實際上是東北歐字母,並不是英語中的 i 。在簡單的文字編輯器裡,想看出這一點點區別幾乎是不可能的。

巧妙利用編譯器對於命名長度的限制

如果編譯器只區分命名的前幾位,比如前8位,那麼就把後面的字母寫得不一樣。比如,其實是同一個變數,有時候寫成 var_unit_update() ,有時候又寫成 var_unit_setup(),看起來是兩個不同的函式呼叫。而在編譯的時候,它們其實是同一個變數 var_unit。

下劃線,真正的朋友

可以拿 _ 和 __ 作為標示符。

混合多語言

隨機地混用兩種語言(人類語言或計算機語言都行)。如果老闆要求使用他指定的語言,你就告訴他你用自己的語言更有利於組織你的思路,萬一這招不管用,就去控訴這是語言歧視,並威脅起訴老闆要求鉅額精神損失賠償。

擴充套件 ASCII 字元

擴充套件 ASCII 字元用於變數命名是完全合法的,包括 ß, Ð, 和 ñ 等。在簡單的文字編輯器裡,除了拷貝/貼上,基本上沒法輸入。

其他語言的命名

使用外語字典作為變數名的來源。例如,可以用德語單詞 punkt 代替 point。除非維護程式碼的程式設計師也像你一樣熟練掌握了德語. 不然他就只能盡情地在程式碼中享受異域風情了。

數學命名

用數學操作符的單詞來命名變數。例如:openParen = (slash + asterix) / equals;

令人眩暈的命名

用帶有完全不相關的感情色彩的單詞來命名變數。例如:marypoppins = (superman + starship) / god;(歡樂滿人間 = (超人 + 星河戰隊)/上帝;)

這一招可以讓閱讀程式碼的人陷入迷惑之中,因為他們在試圖想清楚這些命名的邏輯時,會不自覺地聯絡到不同的感情場景裡而無法自拔。

何時使用 i

永遠不要把 i 用作最內層的迴圈變數。 用什麼命名都行,就是別用i。把 i 用在其他地方就隨便了,用作非整數變數尤其好。

慣例 — 明修棧道,暗度陳倉

忽視 Java 編碼慣例,Sun 自己就是這樣做的。幸運的是,你違反了它編譯器也不會打小報告。這一招的目的是搞出一些在某些特殊情況下有細微差別的名字來。如果你被強迫遵循駝峰法命名,你還是可以在某些模稜兩可的情況下顛覆它。例如,inputFilename 和 inputfileName 兩個命名都可以合法使用。在此基礎上自己發明一套複雜到變態的命名慣例,然後就可以對其他人反咬一口,說他們違反了慣例。

小寫的 l 看上去很像數字 1

用小寫字母 l 標識 long 常數。例如 10l 更容易被誤認為是 101 而不是 10L 。 禁用所有能讓人準確區分 uvw wW gq9 2z 5s il17|!j oO08 `'” ;,. m nn rn {[()]} 的字型。要做個有創造力的人。

把全域性命名重用為私有

在A 模組裡宣告一個全域性陣列,然後在B 模組的標頭檔案裡再宣告一個同名的私有陣列,這樣看起來你在B 模組裡引用的是那個全域性陣列,其實卻不是。不要在註釋裡提到這個重複的情況。

誤導性的命名

讓每個方法都和它的名字蘊含的功能有一些差異。例如,一個叫 isValid(x)的方法在判斷完引數x的合法性之後,還順帶著把它轉換成二進位制並儲存到資料庫裡。

未完待續..

據說上個這樣做的程式設計師已祭天。。

相關文章