# 程式碼的氣味與啟發
---
## 報告內容
- Clean Code 無瑕的程式碼
- 程式碼的氣味與啟發
- 程式碼的原則 及 需注意的部分
- 協助編輯 改善 程式,引導產出更多的程式碼
---
## 原則
- 最小驚奇原則 ```系統的行為應表現的與大部分人預期的相同```
- 如同預期
- 避免猜測、嘗試
- 節省時間及資源
----
- 單一職責原則
- 一個單元、一個任務
- 簡潔有力
- 直覺的理解及運行
- 內聚性
----
- 閱讀性
- 優雅、效率、紀律、整潔
- 愉快的閱讀
- 避免破窗效應理論
- 唯一有效的「程式品質」度量單位:每分鐘罵髒話的次數
- 命名名稱:**有力的表達**
----
- 童子軍規則 ```每次經手程式,都應該使它能比經手前好一些```
- 經手並改善
- 對小事負責
---
## 關鍵詞
----
- 不適當的、廢棄的、被遺棄的、多餘的、被註解掉的
- 不論是註解、函式、變數,都不應該有符合以上關鍵詞
- 善用版控,讓那些應該存在版控裡的資訊,留在板控裡面
- 刪掉它們:有需要時,再從版控提取即可
----
- 反直覺的
- 使一個單元,負責了不應屬於它的任務
- 輸出型參數:參數應用於輸入,而非輸出
- 旗標參數:函數應只負責一個任務,有旗標參數表示負責了超過一個的任務
----
- 反直覺的
- 使一個預期的行為未被實現
- 與上述 最小驚奇原則 連結
- **任何函式或類別** 應該實現其他程式設計師 **合理預期該有的行為**
- 反之,則會破壞程式設計師間的信任 => 需回頭閱讀其他程式
----
- 複雜的
- 建立專案或系統
- 不應需用瑣碎的步驟才能建立
- 不應需用東找西找來尋找程式所需的資源
- 應要能利用一個簡單的指令以完成
----
- 複雜的
- 測試
- 最好的情況:只需按下IDE的某個按鈕
- 最糟的情況:只需於指令環境輸入一個簡單指令
- 其他
- 原始檔使用的語言
---
## 重要且常見的狀況
----
### 標準化
- 同份原始檔存在多種語言
- 遵循標準的慣例
----
### 直覺化
- 最小驚奇法則
- 明顯該有的行為未被實現
- 不一致性
- 垂直分隔
- 減少宣告與使用間的垂直距離
- 人為的耦合
- 兩個模組間「沒有直接目的」的耦合
----
### 直覺化
- 特色留戀
- 類別不應對其他類別的變數或函數感興趣
- 模糊的意圖
- 需要花額外時間以了解
- 函式名稱要說到做到
----
### 正確性
- 在邊界上的不正確行為
- 不要依賴直覺
- 查看所有邊界條件 => 為邊界條件撰寫程式
- 無視安全規範
- 無視警告、忽略失敗的測試 可能可以節省時間
- 卻能造成更大的損害
- 錯置的職責
- 正確的函式在正確的類別下
----
### 正確性
- 不適當的靜態宣告
- 可使用多型替代
- 要精確
- 不要隨意
- 結構勝於常規
----
### 精簡化
- 重複的程式碼
- 一次,就只能一次
- 過多的資訊
- 減少類別所擁有的(方法/變數/實體變數)
- 被遺棄的程式碼
- 不要讓不被需要的程式被編譯
----
### 精簡化
- 選擇型參數
- 單一職責原則
- 了解演算法
- 函式應該只做一件事
- 避免傳遞性導覽
```python=
a.getB().getC().doSomething()
```
----
### 閱讀性
- 雜亂的程式
- 不會被使用的變數
- 不會被呼叫的函式
- 沒有資訊的註解
- 使用具解釋性的變數
- 可讀性
----
### 閱讀性
- 用有名稱的常數取代魔術數字
- 利用 Boolean 變數來封裝其邏輯
- 封裝條件判斷
- 封裝邊界條件
- 避免否定的條件判斷
- 隱藏時序耦合
- 排序函式參數,以凸顯其呼叫次序
----
### 類別設計
```
「高層次的一般概念」 & 「低層次的細節概念」
建立抽象類別 -> 包含高層次的概念
建立衍生類別 -> 包含低層次的概念
```
- 在錯誤抽象層次上的程式碼
- 確保「**所有的** 低層次概念都放在衍生類別內」
- 確保「**所有的** 高層次概念都放在基底類別內」
----
### 類別設計
- 基底類別相依於其衍生類別
- 高層次基底類別,可以 **獨立*** 於低層次衍生類別
- 讓邏輯相依變成實體相依
- 使類別可以自己處理自己的事務
- 德摩特爾法則
- 模組不該知道「關於它所操縱物件的內部運作」
----
#### 類別設計
- 用多型取代 if/else 或 switch/case
- 對於給定的選擇型態,不應該有超過一個以上的 switch 敘述
```java=
program Adhoc;
function Add(x, y : Integer) : Integer;
begin
Add := x + y
end;
function Add(s, t : String) : String;
begin
Add := Concat(s, t)
end;
begin
Writeln(Add(1, 2)); (* 打印"3" *)
Writeln(Add('Hello, ', 'Mammals!')); (* 打印"Hello, Mammals!" *)
end.
```
----
#### 類別設計
- 函式內容應該下降抽象層次一層
- 將不同層次的抽象概念分隔開
```javascript=
public String render() throws Exception
{
StringBuffer html = new StringBuffer("<hr");
if(size > 0)
html.append(" size=\"").append(size + 1).append("\"");
html.append(">");
return html.toString();
}
```
----
#### 類別設計
- 函式內容應該下降抽象層次一層
- 將不同層次的抽象概念分隔開
```javascript=
public String render() throws Exception
{
HtmlTag hr = new HtmlTag("hr");
if (extraDashed > 0)
hr.addAttribute("size", hrSize(extraDashed))
}
private String hrSize(int height)
{
int hrSize = height + 1;
return String.format("%d", hrSize)
}
```
----
#### 類別設計
- 可調整的資料應放置於高階層次
- 常數:如預設值/設定值
- 已知預期應該存在於高階抽象層次上->放在高階處
- 不要把它留在低階處
----
### 命名
- 選擇具描述性質的名稱
- 避免誤導
- 產生有意義的區別
- 在適當的抽象層次選擇適當的命名
- 盡可能使用標準命名法
- 駝峰式大小寫
----
### 命名
- 非模稜兩可的名稱
- 避免雙關語
- 較大範圍的視野使用較長的名字
- 避免編碼
- 匈牙利命名法
- 命名應該描述可能的程式副作用
----
### 測試
- F.I.R.S.T
- Fast
- 快速的測試,促進更多的測試
- Independent
- 依賴掩蓋後續的失敗
- Repeatable
- 任何環境都可執行
- Self-Validating
- 自我驗證
- Timely
- 程式服務測試
----
### 測試
- 不足夠的測試
- 使用涵蓋率工具
- 不要跳過簡單的測試
- 被忽略的測試是對模稜兩可的疑問
- 測試邊界條件
- 在程式錯誤附近進行詳盡的測試
- 失敗的模式是某種啟示
- 測試涵蓋率模式可以是一種啟示
- 測試要夠快速
---
## 心得
----
#### Ærlighed i små ting er ikke nogen lille ting.
#### 在小事情上誠實,可不是一件小事
{"metaMigratedAt":"2023-06-15T22:16:46.324Z","metaMigratedFrom":"Content","title":"程式碼的氣味與啟發","breaks":true,"contributors":"[{\"id\":\"16b1c756-1043-40ce-ac28-4579eb28305d\",\"add\":4384,\"del\":394}]"}