美化java代碼,從合理注釋開始
請停止代碼注釋
“干凈的代碼應該像寫好的散文一樣” - Robert C. Martin
不良代碼的通病就是有很多注釋。這是凌亂的源代碼最明顯的跡象。
每個程序員的目標應該是編寫干凈和富有表現(xiàn)力的代碼,以避免代碼注釋。每個變量,函數(shù)和類的目的應該隱含在其名稱和結(jié)構中。
當其他人讀取您的代碼時,他們不應該閱讀注釋以了解你的代碼正在做什么。命名良好的類和函數(shù)應該引導讀者通過你的代碼,就像一本寫得很好的小說一樣。當讀者看到一個新的類或功能時,他們不應該對他們在里面看到的東西感到困惑難以理解。
請記住,開發(fā)人員的工作時間很少花在編寫代碼上,花在閱讀代碼和理解代碼上的時間要多得多。
注釋掩飾錯誤
在代碼中命名是非常重要的。您應該花費大量精力準確而精確地命名每一段代碼,以便其他開發(fā)人員能夠理解您的代碼。
// 按狀態(tài)查找員工 List<Employee> find(Status status) { ... }
在此示例中,名稱find不夠描述,因此此函數(shù)的作者需要留下描述函數(shù)功能的描述性注釋。當我們看到從另一個模塊調(diào)用的find函數(shù)時,它的作用是一個謎。它發(fā)現(xiàn)了什么?究竟是什么意思?它返回了它發(fā)現(xiàn)的東西嗎?怎么找到它發(fā)現(xiàn)的東西?就像鮑勃叔叔在他的書《Clean Code》中所說,如果你需要寫注釋,你就無法通過代碼表達自己真實的用意。
我們不希望檢查每個函數(shù)上面的注釋,以了解它的作用。
List<Employee> getEmployeesByStatus(Status status) { ... }
現(xiàn)在很明顯能看出來這個函數(shù)的具體作用,這使得注釋變得多余。這讓我想到了注釋糟糕的下一個方式。
冗余注釋
這些混亂了你的代碼,完全沒必要。
//此函數(shù)發(fā)送電子郵件 void sendEmail() { ... } //此函數(shù)發(fā)送電子郵件 public class Employee { ... } / ** * @param title CD的標題 * @param作者CD的作者 * @param track CD上的曲目數(shù) * / public void addCd(String title, String author, int tracks) { ... }
多數(shù)情況是強制冗余。很多公司在每個功能和類別上都要求這一點。如果你的上司要求這樣做,請他們不要。
錯誤的抽象程度
如果您有一個很長的功能或需要記錄代碼的哪一部分做了什么,那么您可能違反了這些規(guī)則:
1.功能應該做一件事。
2.功能應該很小。
這是一個例子
//此函數(shù)計算價格,與銷售額進行比較 //促銷,檢查價格是否有效,然后 //向用戶發(fā)送促銷電子郵件 public void doSomeThings(){ //計算價格 ... ... ... //將計算出的價格與促銷活動進 ... ... ... //檢查計算的價格是否有效 ... ... ... //向用戶發(fā)送促銷信息 ... ... ... }
當你成功地將邏輯的每個部分封裝到一個單獨的函數(shù)中時,代碼不需要注釋就會表現(xiàn)的應該像它的作用描述一樣。
重構如下:
public void sendPromotionEmailToUsers(){ calculatePrices(); compareCalculatedPricesWithSalesPromotions(); checkIfCalculatedPricesAreValid(); sendPromotionEmail(); }
而不是注釋代碼的每個部分,每個邏輯塊應該很好地封裝在它自己的函數(shù)中。
首先,這提高了可讀性。每個代碼塊不必逐行讀取。我們可以簡單地讀取輔助函數(shù)名稱并理解它的作用。如果我們想要了解每個函數(shù)內(nèi)部的更多細節(jié),就能去看具體實現(xiàn)。
其次,它提高了可測試性。在上面的示例中,我們可以為每個函數(shù)單獨進行單元測試。如果不封裝這些單獨的函數(shù),則很難測試較大函數(shù)sendPromotionEmailToUsers()的每個部分。執(zhí)行多個功能的功能很難測試。
最后,它提高了可重構性。通過將邏輯的每個部分封裝到自己的函數(shù)中,將來更改維護更容易,并且單獨功能的函數(shù)會被隔離以僅更改該函數(shù)的行為。當我們使用局部變量的長函數(shù)在整個函數(shù)中持續(xù)存在時,由于函數(shù)的緊耦合,很難在不導致其他地方變化的情況下重構函數(shù)。
注釋掉的代碼
注釋掉的代碼應該被視為roadkill。不要看它,不要聞它,不要問它從哪里來,只是擺脫它。保持它的時間越長,其余代碼聞到的時間就越長......
/ * public void oldFunction(){ noOneRemembersWhyIAmHere(); tryToUnCommentMe(); iWillProbablyCauseABuildFailure(); HAHAHA(); } * /
盡管刪你不刪別人更不敢刪。如果你以后需要它,你可以隨時檢查版本控制系統(tǒng),因為你肯定用了VCS,對嗎?(如果不是當我沒說)
TODO注釋
不要寫TODO注釋,而不僅僅是......做到了嗎?大多數(shù)時候這些注釋都會被遺忘,后來可能變得無關或錯誤。當另一個程序員稍后看到TODO注釋時,他們?nèi)绾沃朗欠襁€需要這樣做?
不過偶爾TODO注釋是好的,如果你正在等待另一個隊友的合并(一般不會太久)。就可以這么做,直到你可以進行修復并提交它。
“當你覺得有必要寫注釋時,首先要嘗試重構代碼,以便任何注釋都變得多余?!?- Martin Fowler
注釋的謊言
當Jimmy在他寫的新功能上面打上注釋時,他認為他正在幫助任何看到他的代碼的未來開發(fā)人員。其實呢他真正在做的是設置一個陷阱。他的注釋可能是彌天大謊(沒有雙關語意圖)蟄伏數(shù)月或數(shù)年沒有被觸及,只是等待成為一個令人討厭的陷阱。然后有一天,在數(shù)百個重構和需求變更之一中,他的注釋從一些遙遠的模塊中失效,但是仍然在錯誤的引導著無數(shù)的接盤俠。
當你更改一行代碼時,你怎么知道你更改的代碼會不會使其他地方的注釋無效?沒有辦法知道
public class User { ... //它包含用戶的名字和姓氏 String name; ... }
然后,需求更改,他們希望將名稱拆分為firstName和lastName。
public class User { ... // 它包含用戶的名字和姓氏 String firstName; String lastName; ... }
注釋現(xiàn)在已經(jīng)錯了。你可以更新注釋以反映更改,但是你是否真的想在每次更改后手動維護所有注釋?你是開發(fā)人員,而不是文檔。
但是這個注釋很容易被注意到并且沒有問題需要改變。但是你很難保證在程序的其他地方,會不會也注釋了這個參數(shù)name是用戶的名字和姓氏。更改一小塊地方的代碼,可能會讓很多的代碼注釋都失效。
讓我們看另一個例子:
//根據(jù)狀態(tài)處理員工 void processEmployees(){ ... List < Employee > employees = findEmployees(statusList); ... } //這會按狀態(tài)列表查找Employees List < Employee > findEmployees(List < String > statusList){ ... }
然后有人被要求更改函數(shù)findEmployees,以便通過名稱列表而不是狀態(tài)列表查找員工。
//根據(jù)狀態(tài)處理員工 void processEmployees(){ ... List < Employee > employees = findEmployees(statusList); ... } //這會按狀態(tài)列表查找Employees List < Employee > findEmployees(List < String > nameList){ ... }
首先,上面的注釋findEmployees已經(jīng)失效,因此需要更改。沒問題,對吧?錯了。
processEmployees上面的注釋也已失效,因此也需要更改。還有多少其他評論被這個小型重構改成無效?這一次更改在源代碼中創(chuàng)建了多少注釋謊言?
替代方案:
void processEmployees(){ ... List < Employee > employees = findEmployeesByName(nameList); ... } List < Employee > findEmployeesByName(List < Name > nameList){ ... }
如果你準確而準確地命名你的函數(shù),則不需要注釋,并且你不會在代碼中散布謊言。
“代碼永遠不會說謊,注釋會。” - 羅恩杰弗里斯
什么時候需要注釋呢
我知道很多開發(fā)人員都是代碼注釋的死硬支持者,對他們來說,我必須承認有時注釋是可以的。不過你每寫一段都應當有充足的理由
復雜表達式
如果您有復雜的SQL或正則表達式語句,請繼續(xù)編寫注釋。在代碼中干凈利落地表達諸如此類的陳述可能很困難。在這些表達式上面添加注釋可以幫助其他開發(fā)人員更好地理解您的代碼。
// 格式匹配kk:mm:ss EEE,MMM dd,yyy Pattern timePattern = Pattern.compile("\\d*:\\d*:\\d* \\w*, \\w*, \\d*, \\d*");
注釋警告
如果你需要警告其他開發(fā)人員這段代碼可能發(fā)生的bug,可以在此代碼附近留下注釋。這些注釋可以充當代碼中神秘行為的先兆,并為你的代碼增加價值。
意圖澄清
如果你實在命名廢,那就要為你沒有能力寫出富有表現(xiàn)力的代碼而負責,并寫下注釋表明自己的意圖。
如果你必須撰寫注釋,請確保它是本地的。遠離其引用的非本地評論注定會失效并變成謊言。引用函數(shù)或變量的注釋應直接位于其上方。警告注釋可以在它引用的代碼的上方或旁邊。如果您的IDE支持注釋突出顯示,請使您的警告注釋從其余代碼中脫穎而出。
最后
我已經(jīng)建立了對代碼注釋的感受。我鄙視他們,但我知道有時他們是需要的。
所以,請停止寫這么多注釋。
本文是作者在推特上看到國外一位大神 布萊恩·諾蘭德 的論述,深以為然因此翻譯后加以修飾進行分享的。希望今后自己的代碼也能像散文一樣優(yōu)雅。
相關文章
SpringBoot 自動掃描第三方包及spring.factories失效的問題解決
這篇文章主要介紹了SpringBoot 自動掃描第三方包及spring.factories失效的問題,本文給大家分享最新解決方法,需要的朋友可以參考下2023-05-05springboot項目實現(xiàn)斷點續(xù)傳功能
這篇文章主要介紹了springboot項目實現(xiàn)斷點續(xù)傳,本文通過示例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-08-08SpringBoot實現(xiàn)的Mongodb管理工具使用解析
這篇文章主要介紹了SpringBoot實現(xiàn)的Mongodb管理工具使用解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2019-09-09SpringBoot中添加監(jiān)聽器及創(chuàng)建線程的代碼示例
這篇文章主要介紹了SpringBoot中如何添加監(jiān)聽器及創(chuàng)建線程,文中有詳細的代碼示例,具有一定的參考價值,需要的朋友可以參考下2023-06-06Maven編譯錯誤:程序包com.sun.*包不存在的三種解決方案
J2SE中的類大致可以劃分為以下的各個包:java.*,javax.*,org.*,sun.*,本文文章主要介紹了maven編譯錯誤:程序包com.sun.xml.internal.ws.spi不存在的解決方案,感興趣的可以了解一下2024-02-02