---
# System prepended metadata

title: 第三章 函式
tags: [Tag(Clean Code)]

---

# 第三章 函式

###### tags: `Tag(Clean Code)`

## 定義：

不應該讓函式有==冗長==和==重複==的和==無意義==的字串出現在程式碼中，應該要讓維護者在不清楚全部細節狀況下可以能夠了解函式做的事，也就是需要易讀好懂的函式程式碼。

### 簡短！
#### 區塊（Block）和縮排（Indenting）
if、else、while及其他函是都應該只有一行，而這一行通常是==函式呼叫敘述==，函式不應該大到巢狀式結構和超過兩層。
要讓函式只做一件事，做好一件事。

> 「將函式維持在單一層次的抽象概念」。
> 學習這個技巧很重要，讓函式保持簡短的關鍵。
> 

#### 如何知道只做一件事

觀察函式是否超過「一件事」，看是否能提煉出另一種新的函式。

1. **只做一件事**
如果函式只做了名稱下「同一層抽象概念」的幾個步驟，那麼此函式就算是只做一件事。

2. **做很多事**
函式內的段落，被拆解成不同段落，ex:宣告區（declarations）、初始區(initializations)、過濾區（sieve），這就是超過一件事，做一件事的函式是無法被分出做不同事情的段落。

3. 每個函式只有一層抽象概念 
    * 高層次 ->  getHtml()
    * 中層次 -> 
    includeSetupAndTeardownPages(pageData, isSuite)
==String pagePathName = PathParser.reader(pagePath)==
    * 低層次 -> 
    includeSetupAndTeardownPages(pageData, isSuite)
    ==.append("\n")==
    

**一層抽象概念範例**：
做了三件事
    1. 判斷此頁面是否為測試頁面
    2. 如果是測試頁面，納入設定與拆解步驟
    3. 頁面轉換Html網頁
但為什麼符合一件事的概念，因為此函式三步驟都符合名稱下的同一層抽象概念中。

> ==閱讀此函式==：
> RenderPageWithSetupsAndTeardowns，檢查此頁面是否為測試頁面，如果是就納入設定與拆解步驟，不論什麼情況下都轉換成Html網頁

```
public static String renderPageWithSetupsAndTeardowns(PageData pageData , boolean isSuite) throws Exception{
    if(isTestPage(pageData)){
        includeSetupAndTeardownPages(pageData, isSuite)
    }
    return pageData.getHtml();
}
```

4. **由上而下閱讀程式碼：降層準則**
閱讀程式可以依照看到的一連串函式，對應著抽象層次降層閱讀。


### Switch 敘述

switch 敘述總是在做N件事，如何做到永遠不重複，可以用多型（Polymorphism來達到目的

==問題範例==：
```
public Money calculatePay(Employee e) throw InvalidEmployee
    switch(e.type){
        case COMMISSIONED:
            return calculatePayCommissionedPay(e);
        case HOURLY:
            return calculateHourlyPay(e);
        case SALARIED:
            return calculateSalariedPay(e);
       default:
           throw new InvalidEmployeeType(e.Type);
    }
}
```
問題：
1. 太冗長，加入新職員型態時，會變得更長。
2. 做超過一件事
3. 可能因為一個理由改變函式，違反[單一職責原則](https://zh.wikipedia.org/wiki/%E5%8D%95%E4%B8%80%E5%8A%9F%E8%83%BD%E5%8E%9F%E5%88%99)
4. 當有新的型態加入後，違反[開閉原則(OCP)](https://ithelp.ithome.com.tw/articles/10211845)
5. 程式中可能還有相同的


---

竟量讓一個敘述中出現一個，而且用來產生多型物件，並藏匿在某個繼承關係之下。

==利用多型改善範例==
```
public abstract class Employee(){
    public abstract boolean isPayday();
    public abstract Money CalculatePay();
    public abstract void deliverPay(Money pay);
}

public interface EmployeeFactory(){
    public Employee makeEmployee(EmployeeRecord r) throws InvalidEmployeeType;   
}

public class EmployeeFactoryImpl implements EmployeeFactory{
    
    public Employee makeEmployee(EmployeeRecord r) throws InvalidEmployeeType{
        switch(r.Type){
          case COMMISSIONED:
             return calculatePayCommissionedPay(e);
          case HOURLY:
             return calculateHourlyPay(e);
          case SALARIED:
             return calculateSalariedPay(e);
          default:
            throw new InvalidEmployeeType(e.Type);
        }
    }

}
```
### 使用具有描述能力的名稱

別害怕取較長名字，一個較長但具有描述的名稱，比較短但難以理解得名稱還要好。

### 函式的參數

函數的參數理想為零個(零參數函式;niladic)，其次一個(單參數函式;dyadic)，再不然三個（三參數函式;triadic），如果要使用超過三個參數（多參數函式; polyadic），必須要有特別理由，否則不應該如此，原因是要讓函式容易理解。

輸出型參數（回傳值;return value）和輸入型參數