代碼整潔之道閱讀筆記一(命名、格式)

一、有意義的命名

1. 名副其實

名字能讓人理解變量是用來做什麼的,比如 daysSinceCreation比 d 用來表
示消逝的時間更好。

2. 避免誤導

不用具有特殊含義的詞語,比如list,除非它真的是個list
不用 l o

3. 做有意義的區分

不加沒有作用的區分或者前綴後綴,比如money和moneyAmount沒有區
別。

4. 使用讀的出來的名稱

不要瞎造詞

5. 使用可搜索的名稱

名稱可以長,但要方便搜索,不要用e 等很短的,單字母名稱 如 i只能用於
方法內變量,名稱的長短應與其作用域大小相對應。

6. 避免使用編碼

不需要加Hn phoneString等

7. 類名

類名和對象名應該是名詞或名詞短語,如Customer  WikiPage Account。
不應當是動詞

8. 方法名

方法名應當是動詞或動詞短語,如postPayment save等,訪問器和修改器
和斷言應該加get set is
重載構造器時,應使用描述了參數的靜態工廠方法名,如
Complex fulcurmPoint = Complex.FromRealNumber(23.0) 
好於
Complex fulcrumPoint = new Complex(23.0)

9. 每個概念應該對應一個單詞

比如Controller Manager driver 同種類型的操作用一個專用術語就可以

10. 不用雙關語

比如應該用append的地方用到了add

11. 使用解決方案領域名稱,實在不行使用所涉及領域的名稱

可以使用專業詞語 比如JobQueue來命名,實在不行也可以用涉及到的問
題來命名。

12. 添加有意義的語境

比如firstName lastName street 儘量讓語境內的變量在一個語境下面,並
能讓別人知道這是在幹什麼,也能順藤摸瓜猜出其他變量的含義

二、函數

1. 短小

函數不要太長,太長的代碼塊不要放進去,if while其中的代碼塊應該只有
一行,比如是一個函數調用語句。不要有太深的嵌套結構。

2. 只做一件事

只做一件事,做多件事的話可以拆分成多個函數,幾十行最佳。做多件事
可以用上一個抽象層級包含起來。

3. 每個函數一個抽象層級

應該有一個自頂向下的代碼閱讀規則,細節和調用方不要放在一起,自頂
向下應該是一個樹形結構,葉子節點應該在同一層上,上面都是對它的調
用。
其實這點很難做到,比如上面調用了一個函數,下面進行一個一行的操
作,不可能爲這一行單獨拆分成一個函數,所以我認爲拆分原則還是要根
據代碼的作用,比如這段代碼要做一件事情,即使再短也最好拆分,如果
只是一個格式轉換,那就沒必要單獨拆分。總之是爲了代碼的可讀性。

4. switch語句

可以用來創建多態對象

5. 使用描述性的名稱

爲每個只做一件事的小函數取個好名字,函數越小,功能越集中。就越容
易起名字。不要害怕長名稱,長而具有描述性的名稱,比長註釋要好。

6. 函數參數

• 最好的函數是無參
• 參數和函數名位於不同的抽象層級,給閱讀代碼帶來困難,需要在閱讀代
碼時候去了解不重要的細節。
• 不要向函數內傳標誌參數,比如true false ,因爲這會代表函數不止做了
一件事。而且要在上層和下層判斷兩次邏輯,爲什麼不統一判斷一次呢
Eg:調用處:if() do(true) else do(false)
函數do:if(true) {}  else {}
這樣邏輯就重複了,修改時也要一起修改。
• 二元參數如果是比較自然的,符合規律的,比如座標(x,y),是可以接受
的,一定要按照順序來。
• 參數對象,如果需要一堆參數,那就說明有些參數可以封裝成類了。
Eg:makeCircle(double x, double y, double radius);
可以改成 makeCircle(Point center, double radius)

7. 無副作用

上面說到了函數只做一件事,並且在函數名中最好體現出函數要做什麼
事,不要偷偷做了某件事,但是調用者不知道,這就是副作用。
最好就不要違反函數只做一件事的原則。

8. 指令和詢問分離

不要一個函數既做某些事情,又要返回不相干的結果。
操作和查詢應該是分開的。可以是先查詢,在指令。

9. 使用異常替代返回錯誤碼

• 如果函數會返回錯誤碼的話,可能會導致深層嵌套結構,要求調用方必須
馬上處理錯誤。
所以應該用異常來替代返回錯誤碼,這樣錯誤處理代碼就能分離出來
• 抽離try/catch
Try/catch代碼塊內的內容會弄亂代碼結構,因爲catch裏的內容其實不屬於
主體代碼,將錯誤處理和正常流程混爲一談,所以最好將其中的內容抽離
出來,
• 錯誤處理就是一件事
函數應該只做一件事,錯誤處理就是一件事,所以處理錯誤的函數不應該
在finally後面還有其他的東西
• Error.java錯誤類 導致依賴  不應該存在
錯誤碼的存在,意味着某處有個類或者枚舉,定義了所有的錯誤碼
這意味着其他類都要引入這個錯誤類,並且枚舉修改時,其他類都要重新
編譯

而使用異常替代錯誤碼,新的異常就可以從異常類中派生出來,無需重新編譯,而且解除了依賴

10. 別重複自己

有的算法或代碼塊會在很多函數內出現,可能會比較小,也不容易發現。
這樣代碼會臃腫,而且算法改變時也需要修改四次,可能會導致忽略或忘
掉。如果寫代碼時發現需要copy一段代碼,那麼首先考慮能不能抽離爲函
數。
消滅重複。

11. 怎麼寫出這樣的代碼?

首先要認識到,不可能在沒寫時就已經設計好代碼的結構,所以開始就隨
便寫吧,先實現功能,不要在開始就被規則束縛,然後爲每個函數配好單
元測試,這時就開始打磨代碼吧,保證測試通過。

三、註釋

1. 註釋不能美化糟糕的代碼

糟糕的代碼需要寫註釋才能弄清楚,但是最好是把代碼弄乾淨。帶有少量
註釋的整潔有表達力的代碼比帶有大量註釋的零碎複雜的代碼更有用。

2. 用代碼來闡述

有時用代碼就能解釋意圖,只需要創建一個描述與註釋同一事物的註釋即
可。
比如把一段判斷封裝爲函數
// check if the employee is eligible for full benefite
If ((employee.flags & HOURLY_FLAY) && (employee.age > 65))

改爲
 if(employee.isEligibleForFullBenefits)

3. 好註釋

有哪些註釋是必要有用的呢?
	a. 法律信息,這類一般放在頭部
	b. 提供信息的註釋,這種一般可以用更好的方法名代替
	c. 對意圖的解釋,比如有兩種解決辦法,使用了某一種的原因
	d. 闡釋,比如有一段判斷很複雜,但是目的很簡單,但是最好是讓返回
	值本身就足夠清楚。但有時調用了外部庫,自己不能修改,那就可以幫
	它加闡釋。
	比如: a.compareTo(a) == 0 ; // a == a
	e. 警示,提示某段代碼有何風險。
	f. TODO註釋,但是這類要留出時間定期查看和刪除。
	g. 放大,有些地方可能看起來不合理,爲了避免被改掉,加一段代碼提
	示它的重要性。
	h. 公共API中的javadoc

4. 壞註釋

	a. 喃喃自語
	b. 多餘的註釋,簡單的地方就不需要加註釋。
	c. 誤導性數值:不精確,錯的。
	d. 循軌式註釋
	e. 日誌式註釋,因爲有了源代碼控制系統,所以這類不需要加。
	f. 廢話註釋,也是很簡單的函數就不需要加註釋。
	g. 錯誤的
	h. 位置標記  比如 ///////////////////////////a///////
	i. 括號後邊的註釋,比如while  else
	j. 歸屬與署名,也因爲有源代碼控制系統,所以不需要加。
	k. 註釋掉的代碼,別人也不敢刪,要刪就刪掉,源代碼控制系統可以恢
	復。
	l. html註釋,沒有用。
	m. 信息過多

四、格式

1、垂直格式

  1. 儘量用200~500行爲單個文件。

  2. 源碼應該像報紙一樣:上面是大綱,下面是細節。最頂部應該是高層次概念和算法,細節應該依次向下展開,放在我們代碼裏意思就是:public方法應該放在上面,private方法應該放在下面。應該是一個總分結構。

  3. 概念上垂直方向的區隔
    代碼中是需要一些空行的,方法間必須要有空行,方法中:兩段功能不同的代碼中也需要空行,以區別不同的代碼段,但這個尺度需要掌握好,空行特別多時也會影響閱讀。所以若干很短比如只有一行的代碼段就無需再空行了
    String name = “A”;
    Int age = 10;
    String sex = “男”;

    User user = new User();
    User.setName(name);
    User.setAge(age);
    User.setSex(Sex);

    Mapper.insertUser(user);

  4. 垂直方向上的靠近
    與上面相反,一些功能或概念相似的代碼段是應該放在一起的,比如我上面的代碼中把變量初始化放在一起,new一個對象的過程放在一起。這樣很容易知道它們是相似的。在類中也應該是這樣,私有變量放在一起,公有變量放在一起。

  5. 垂直距離
    關係密切的概念應該相互靠近,條件是在同一個文件中。其實並不應該把關係密切的概念放在不同文件,所以應該避免使用protexted變量。
    a. 變量的聲明應該靠近其使用位置,比如我們寫循環的控制變量一般就放在循環上面。
    b. 實體變量應該在類的頂部聲明,這好像是約定俗成,所以大家都會去頂部找實體變量。有些規範會放在中間,雖然靠近了使用位置,但其實更不好找。
    c. 相關函數,某個函數調用了另外一起就應該把它們放在一起,而且調用者應該儘可能放在被調用者上面,這點一定要適應。

2. 水平格式

  1. 寬度?
    以前的規範是80個字符,現在雖然屏幕變寬了,但是最好也不要太寬,超過120個字符有時就需要滾動了。
  2. 水平方向上的區隔與靠近
    使用空格連接緊密相關的事物,把相關性較弱的事物分隔開。
  • a. 賦值操作符周圍加空格字符。因爲空格可以區分 = 左右變量。

  • b. 函數名和左括號之間不加空格,因爲函數和參數密切相關.
    Public static root(double a, double b, double c);{
    Return (a*b) + c;
    }
    可以看到乘法之間沒加空格,因爲優先級較高,這樣讀起來比較舒服。

      總之加不加空格,就看分隔符兩邊的關係,如果覺得需要分開提高可閱讀性,就加空格,如果覺得是很緊密的,就不加空格。
    
  • c. 水平對齊
    以前在聲明一些相同的變量會對齊,這樣也方便批量操作。
    Private socket socket;
    Private a a;
    其實沒什麼用,中間用空格隔開就好。

  • d. 縮進
    這個沒什麼說的,日常都會縮進,方便了解代碼結構。

  • e. 空範圍
    儘量不適用while 或 for 中爲空

  1. 團隊規則
    一個系統中應使用同樣的代碼規範。

五、對象和數據結構

  1. 私有變量應該通過get set方法,這樣不會暴露內部實現,自己也可以替換實現方法,如果都暴露出去就不能隨意更改。
    這是關係到抽象關係,因爲應該暴露的是抽象接口,而不是本身的數據細節,這是以抽象形態展示數據。所以也不能亂加get set
  2. 對象和數據結構之間的反對稱性
    其實就是面向過程和麪向對象之間的差異:
    面向過程容易添加函數,不容易添加數據結構。
    面向對象 容易添加數據結構,但添加函數需要每個都添加
  3. 得墨忒定律
    模塊不應瞭解它所操作的對象的內部情形
    也就是說對象應該隱藏數據,暴露操作(需要注意,通過存取器也暴露了數據)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章