CleanCodeNotes
一、有意義的命名及函式
- 類名:使用名詞,方法名:動詞
- 別用雙關語:避免將同一單詞用於不同目的。eg. add
- 使用解決方案領域名稱。eg. JobQueue;如果不能使用程式設計師熟悉的術語來給手頭的工作命名,就採用從所涉問題而來的名稱。
- 函式應短小(每行150字元,不超過100行,20行封頂最佳)。
- 函式只做一件事,同設計模式一樣,應儘量遵循單一權責原則及開閉原則。
- 每個函式中的語句,理應都要在同一抽象層級上。
- 沃德原則:如果每個例程都讓你感到深合己意,那就是整潔程式碼(並且不要怕長名稱方法名)
- 函式的引數:最理想的引數數量是零,其次是一,再次是二,儘量避免三。有足夠特殊的理由才能用三個以上引數(阿里巴巴Java開發手冊:相同引數型別,相同業務含義,才可以使用 Java 的可變引數,可變引數放在最後,儘量不用可變引數,避免使用 Object)
- 標識引數:醜陋不堪,render(Boolean isSuite)代表了該函式在標識為true會這樣做,為false會那樣做。應該一分為二——renderForSuite()和renderForSingleTest()
- 二元及三元函式:在當單個值的有序組成部分不確定時,多元函式並不是好的選擇
- 引數物件:如果函式看來需要兩個、三個或三個以上引數,就說明其中一些引數應該封裝為類了。
Circle makeCircle(double x,double y,double radius)
Circle makeCircle(Point center,double radius)
後者優於前者
- 函式與引數:應是一種非常良好的動詞/名詞對形式
write(name) - 無副作用:在執行函式時,應不該造成其他部分的更改
- 分隔指令與詢問:函式要麼做什麼事,要麼回答什麼事
- 使用異常代替返回錯誤程式碼,抽離Try/Catch程式碼塊。Try/Catch程式碼塊醜陋不堪,搞亂了程式碼結構,最好把其中內容單獨寫成一個函式。
二、註釋
- 註釋並不能美化糟糕的程式碼,最好用程式碼來闡述
- TODO 是一種程式設計師認為應該做,但由於某些原因目前還沒做的工作,它可能是要提醒刪除某個不必要的特性,或者要求他人注意某個問題等。
- 壞註釋:
喃喃自語、多餘的註釋、誤導性註釋、循規式註釋、日誌式註釋、廢話註釋。(p55-p61) - 不應當有的註釋:位置標記、括號後面的註釋、歸屬與署名、註釋掉的程式碼、HTML註釋、非本地資訊、資訊過多、不明顯的聯絡(p62-p65)
- 非公共程式碼中的Javadoc
三、格式
- 垂直格式
單個檔案程式碼行數fitnese多在200行到500行之間
(作者認為儘量精簡程式碼在單個檔案中,並且檔案之間程式碼行數最小與最大值差距不要過大)
- 向報紙學習(自上而下,內容逐次展開)。實際程式設計中注意很難注意這個
-
適當的換行
- 方法與方法之間
- 變數與方法之間
- import 與類之間
- import 系統類和匯入包中的類或者自定義類之間
-
垂直距離
- 變數宣告應儘可能靠近其使用位置
- 實體變數應放在類的頂部宣告
- 相關函式應儘量放在一起,一個方法呼叫了該類中的另一個方法,應儘量放在一塊
- 概念相關的程式碼應該放在一起,過載的方法也算
-
橫向距離
(一行程式碼所用字元 [阿里巴巴java開發手冊中有定義],水品對齊,水平方向上的取個與靠近-不認同)- 同一層級的程式碼應該縮排至同一個層級(Python中採用強制縮排)
四、物件和資料結構
- 程式導向式程式碼與物件導向式程式碼(p89-p90)
對於物件導向較難的事,對於過程式程式碼卻較容易,反之亦然。 -
得墨忒耳律認為,類 C 的方法f只應該呼叫以下物件的方法:
- C
- 由 f 建立的物件
- 作為引數傳遞給 f 的物件
- 由 C 的實體變數持有的物件
方法不應呼叫有任何函式返回的物件的方法,換言之,只跟朋友談話,不與陌生人談話。(實際生產環境用jfinal service 調 dao 是用的該實體類的靜態內部物件,與ssm有不同)
- DTO(Data Transfer Objects) 裡最好不要有業務規則程式碼。
五、錯誤處理
- 使用異常而非返回碼
- 先寫 try – catch – finally
- 某種意義上,try 程式碼塊就像是事務,catch 程式碼塊將程式維持在一中持續狀態,無論 try 程式碼庫中發生了什麼均如此。
- 給出異常發生的環境說明。
- 別傳遞 null 值:可以用斷言代替判空。 assert p1 != null: “p1 should not be null”;
六、邊界
- 學習 log4j
- 使用上尚不存在的程式碼:介面卡模式(作者採用的)
七、類
- 類的組織:公共靜態變數->私有靜態變數->私有實體變數。很少出現公共實體變數
- 類應該短小:對於類,我們採用不同的衡量方法,計算權責。
- 單一權責原則:類或模組應有且只有一條加以修改的理由。
- 內聚:類應該只有少量實體變數,類中的每個方法都應該操作一個或多個這種變數。
- 文中舉例說明了一個 Sql 類的重構。將一個 Sql 類中所有的 CRUD 方法抽象成一個 generate 方法,並由相應的 CRUD 類繼承。(和現在 jfinal 的使用方法不一樣,雖然違反了設計模式開閉原則。但我認為對中小型專案開發,反設計模式更為便捷)
八、系統
- 依賴注入(Dependency Injection DI),控制反轉(Inversion of Control IOC):可以實現分離構造與使用,控制反轉將第二權責從物件中拿出來,轉義到另一個專注於此的物件中,從而遵循了單一權責原則。因為初始設定是一種全域性問題,這種授權機制通常要麼是 main 例程,要麼是有特定的目的的容器(Spring 中採用 ConcurrentHashMap 作為該容器)。
- 擴容:為達到鬆耦合,採用 AOP 實現某些程式碼的行為修改。(最終的系統,系統內的模組達到高內聚低耦合的狀態,並且易擴充套件)
- Java代理:詳情 p148
九、併發程式設計
- 併發是一種解耦策略。它幫助我們把做什麼(目的)和 何時(時機)做分解開。
- 常見的迷思與誤解
- 併發總能改進效能?
併發有時候能改進效能,單隻在多個執行緒或處理器之間能分享大量等待時間的時候管用。事情沒那麼簡單。(編排考生時,又開了一個執行緒,編排所有考生) - 編寫併發程式無需修改設計
事實上,併發演算法的設計有可能與單執行緒系統的設計極不相同。目的與時間的解耦往往對系統結構產生巨大影響。 - 採用Web 或 EJB 容器的時候,理解併發問題並不重要(實際開發中,面向servlet開發。。。)
- 併發防禦原則
- 單一權責原則
建議將併發相關程式碼與其他程式碼進行分離 - 限制資料作用域
產生併發問題的原因,其實就是共享變數,在 JMM 中,共享變數存在於主存中,多個執行緒從主存中拷貝共享變數到當前執行緒中,線上程內部進行對變數的操作。 CPU 給每個執行緒分配時間片,當執行緒 A 將共享變數刷回主存中前,執行緒 B 可能得到了 CPU 分配的時間片,由於 A 執行緒未及時重新整理共享變數至主存,B 執行緒取得的是未經過 A 執行緒修改的值,會造成資料與理想情況不一致。最經典的多執行緒入門,進行自增操作時,如果開了多執行緒進行該操作,結果永遠是小於等於 期望值。例如`
java
int num=0;
for(int i=0;i<100;i++){
num++;
}
}