2019-09-19 09:41:53 1487瀏覽
作為一名程序員,日常工作就是需要寫代碼,你代碼的編寫水平如何很大程度上就是反應你的工作能力如何。寫出整潔的代碼,是每個程序員的追求。《cleancode》指出,要想寫出好的代碼,首先得知道什么是骯臟代碼、什么是整潔代碼;然后通過大量的刻意練習,才能真正寫出整潔的代碼。本篇文章扣丁學堂Java培訓小編就給讀者們分享一下代碼整潔vs代碼骯臟,希望對小伙伴們有幫助。
其中,我最喜歡的是表達力(Expressiveness)這個描述,這個詞似乎道出了好代碼的真諦:用簡單直接的方式描繪出代碼的功能,不多也不少。
下面是書中的一個示例代碼,展示了命名對代碼質量的提升
#badcode defgetItem(theList): ret=[] forxintheList: ifx[0]==4: ret.append(x) returnret #goodcode defgetFlaggedCell(gameBoard): '''掃雷游戲,flagged:翻轉''' flaggedCells=[] forcellingameBoard: ifcell.IsFlagged(): flaggedCells.append(cell) returnflaggedCells
1.2避免誤導
不要掛羊頭賣狗肉
不要覆蓋慣用縮略語
這里不得不吐槽前兩天才看到的一份代碼,居然使用了l作為變量名;而且,user居然是一個list(單復數都沒學好!!)
1.3有意義的區分
代碼是寫給機器執行,也是給人閱讀的,所以概念一定要有區分度。
# bad def copy(a_list, b_list): pass # good def copy(source, destination): pass
1.4使用讀的出來的單詞
如果名稱讀不出來,那么討論的時候就會像個傻鳥
1.5使用方便搜索的命名
名字長短應與其作用域大小相對應
1.6避免思維映射
比如在代碼中寫一個temp,那么讀者就得每次看到這個單詞的時候翻譯成其真正的意義
二、注釋
有表達力的代碼是無需注釋的:Theproperuseofcommentsistocompensateforourfailuretoexpressourselfincode.
注釋的適當作用在于彌補我們用代碼表達意圖時遇到的失敗,這聽起來讓人沮喪,但事實確實如此。Thetruthisinthecode,注釋只是二手信息,二者的不同步或者不等價是注釋的最大問題。
書中給出了一個非常形象的例子來展示:用代碼來闡述,而非注釋
bad // check to see if the employee is eligible for full benefit if ((employee.flags & HOURLY_FLAG) && (employee.age > 65)) good if (employee.isEligibleForFullBenefits())
因此,當想要添加注釋的時候,可以想想是否可以通過修改命名,或者修改函數(代碼)的抽象層級來展示代碼的意圖。
當然,也不能因噎廢食,書中指出了以下一些情況屬于好的注釋
法務信息
對意圖的注釋,為什么要這么做
警示
TODO注釋
放大看似不合理之物的重要性
其中個人最贊同的是第2點和第5點,做什么很容易通過命名表達,但為什么要這么做則并不直觀,特別涉及到專業知識、算法的時候。另外,有些第一感覺“不那么優雅”的代碼,也許有其特殊愿意,那么這樣的代碼就應該加上注釋,說明為什么要這樣,比如為了提升關鍵路徑的性能,可能會犧牲部分代碼的可讀性。
最壞的注釋就是過時或者錯誤的注釋,這對于代碼的維護者(也許就是幾個月后的自己)是巨大的傷害,可惜除了codereview,并沒有簡單易行的方法來保證代碼與注釋的同步。
三、函數
3.1函數的單一職責
一個函數應該只做一件事,這件事應該能通過函數名就能清晰的展示。判斷方法很簡單:看看函數是否還能再拆出一個函數。
函數要么做什么dosth,要么查詢什么querysth。最惡心的就是函數名表示只會querysth,但事實上卻會dosth,這使得函數產生了副作用。比如書中的例子
public class UserValidator { private Cryptographer cryptographer; public boolean checkPassword(String userName, String password) { User user = UserGateway.findByName(userName); if (user != User.NULL) { String codedPhrase = user.getPhraseEncodedByPassword(); String phrase = cryptographer.decrypt(codedPhrase, password); if ("Valid Password".equals(phrase)) { Session.initialize(); return true; } } return false; } }
3.2函數的抽象層級
每個函數一個抽象層次,函數中的語句都要在同一個抽象層級,不同的抽象層級不能放在一起。比如我們想把大象放進冰箱,應該是這個樣子的:
def pushElephantIntoRefrige(): openRefrige() pushElephant() closeRefrige()
函數里面的三句代碼在同一個層級(高度)描述了要完成把大象放進冰箱這件事順序相關的三個步驟。顯然,pushElephant這個步驟又可能包含很多子步驟,但是在pushElephantIntoRefrige這個層級,是無需知道太多細節的。
當我們想通過閱讀代碼的方式來了解一個新的項目時,一般都是采取廣度優先的策略,自上而下的閱讀代碼,先了解整體結構,然后再深入感興趣的細節。如果沒有對實現細節進行良好的抽象(并凝練出一個名副其實的函數),那么閱讀者就容易迷失在細節的汪洋里。
某種程度看來,這個跟金字塔原理也很像
每一個層級都是為了論證其上一層級的觀點,同時也需要下一層級的支持;同一層級之間的多個論點又需要以某種邏輯關系排序。pushElephantIntoRefrige就是中心論點,需要多個子步驟的支持,同時這些子步驟之間也有邏輯先后順序。
3.3函數參數
函數的參數越多,組合出的輸入情況就愈多,需要的測試用例也就越多,也就越容易出問題。
輸出參數相比返回值難以理解,這點深有同感,輸出參數實在是很不直觀。從函數調用者的角度,一眼就能看出返回值,而很難識別輸出參數。輸出參數通常逼迫調用者去檢查函數簽名,這個實在不友好。
向函數傳入Boolean(書中稱之為FlagArgument)通常不是好主意。尤其是傳入TrueorFalse后的行為并不是一件事情的兩面,而是兩件不同的事情時。這很明顯違背了函數的單一職責約束,解決辦法很簡單,那就是用兩個函數。
3.4Dontrepearyourself
在函數這個層級,是最容易、最直觀實現復用的,很多IDE也難幫助我們講一段代碼重構出一個函數。
不過在實踐中,也會出現這樣一種情況:一段代碼在多個方法中都有使用,但是又不完全一樣,如果抽象成一個通用函數,那么就需要加參數、加ifelse區別。這樣就有點尷尬,貌似可以重構,但又不是很完美。
造成上述問題的某種情況是因為,這段代碼也違背了單一職責原則,做了不只一件事情,這才導致不好復用,解決辦法是進行方法的細分,才能更好復用。也可以考慮templatemethod來處理差異的部分。
四、測試
非常慚愧的是,在我經歷的項目中,測試(尤其是單元測試)一直都沒有得到足夠的重視,也沒有試行過TDD。正因為缺失,才更感良好測試的珍貴。
我們常說,好的代碼需要有可讀性、可維護性、可擴展性,好的代碼、架構需要不停的重構、迭代,但自動化測試是保證這一切的基礎,沒有高覆蓋率的、自動化的單元測試、回歸測試,誰都不敢去修改代碼,只能任其腐爛。
即使針對核心模塊寫了單元測試,一般也很隨意,認為這只是測試代碼,配不上生產代碼的地位,以為只要能跑通就行了。這就導致測試代碼的可讀性、可維護性非常差,然后導致測試代碼很難跟隨生產代碼一起更新、演化,最后導致測試代碼失效。所以說,臟測試-等同于-沒測試。
因此,測試代碼的三要素:可讀性,可讀性,可讀性。
對于測試的原則、準則如下:
Youarenotallowedtowriteanyproductioncodeunlessitistomakeafailingunittestpass.沒有測試之前不要寫任何功能代碼
Youarenotallowedtowriteanymoreofaunittestthanissufficienttofail;andcompilationfailuresarefailures.只編寫恰好能夠體現一個失敗情況的測試代碼
Youarenotallowedtowriteanymoreproductioncodethanissufficienttopasstheonefailingunittest.只編寫恰好能通過測試的功能代碼
測試的FIRST準則:
快速(Fast)測試應該夠快,盡量自動化。
獨立(Independent)測試應該應該獨立。不要相互依賴
可重復(Repeatable)測試應該在任何環境上都能重復通過。
自我驗證(Self-Validating)測試應該有bool輸出。不要通過查看日志這種低效率方式來判斷測試是否通過
及時(Timely)測試應該及時編寫,在其對應的生產代碼之前編寫
想了解更多內容的小伙伴可以登錄扣丁學堂官網咨詢。想要學好Java開發小編給大家推薦口碑良好的扣丁學堂,扣丁學堂有專業老師制定的Java學習路線圖輔助學員學習,此外還有與時俱進的Java課程體系和Java視頻教程供大家學習,想要學好Java開發技術的小伙伴快快行動吧。扣丁學堂Java技術交流群:850353792。
【關注微信公眾號獲取更多學習資料】 【掃碼進入JavaEE/微服務VIP免費公開課】