[Clean Code] Chapter 3: 函數!

函數


1、Small! 小巧!

The first rule of functions is that they should be small.

The second rule of functions is that they should be smaller than that.


2、Do One Thing! 只做一件事!

FUNCTIONS SHOULD DO ONE THING.

THEY SHOULD DO IT WELL.

THEY SHOULD DO IT ONLY.

函數要只做一件事,並做好它!


怎麼知道什麼是 DO ONE THING

如果你能從函數A中提取另一個函數B ,而且這個函數B不是A的另一個實現


3、One Level of Abstraction per Functions 函數邏輯應處於同一層抽象


4、The Stepdown Rule 步步深入原則

提取不同的抽象層次,確保function do one thing.


5、switch

對於switch, 如果使用在生成具體對象,並且隱藏在繼承關係下,是可以接受的,因爲系統其他部分將看不到他們。

對比下面代碼

// 代碼片段1,如果不使用抽象基類繼承做,會導致在其他相關函數也會有switch的判斷!!!!
// 譬如: isPayday(Employee e, Date date)
public Money calculatePay(Employee e) throws InvalidEmployeeType {
    switch (e.type) {
        case COMMISSIONED:
            return calculateCommissionedPay(e);
        case HOURLY:
            return calculateHourlyPay(e);
        case SALARIED:
            return calculateSalariedPay(e);
        default:
            throw new InvalidEmployeeType(e.type);
    }
}
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;
}

// 將 switch 使用在生成具體的對象上
public class EmployeeFactoryImpl implements EmployeeFactory {
    public Employee makeEmployee(EmployeeRecord r) throw InvalidEmployeeType {
        switch (r.type) {
        case COMMISSIONED:
            return new CommissionedEmployee(r) ;
        case HOURLY:
            return new HourlyEmployee(r);
        case SALARIED:
            return new SalariedEmploye(r);
        default:
            throw new InvalidEmployeeType(r.type);
        }
    }
}

6、起個好名字

描述準確,不怕長,和其他函數統一動名詞


7、參數

0參數:好極了

1參數:挺好

2參數:還不錯

3參數:如果允許,我應該避免這麼做

3+參數: never !


8、Flag 參數

傳參true or false 做不同的事情???直接違背do one thing原則。

我們可以將他分割成兩個函數來解決此問題。

// 例如
render(boolean isSuite);
|
+-- renderForSuite();
|
+-- renderForSingleTest();

9、 拒絕Side Effects(副作用)!

// 事例代碼
public class UserValidator {
    private Cryptographer cryptographer;

    public boolean checkPassword(String userName, String password) {
        User user = UserGateway.findByName(userName);
        if (user != User.NULL) {
            String codedPhrase = user.getPhraseEncodedByPassword();
            String phrase = cryptographer.decrypt(codedPhrase, password);
            if ("Valid Password".equals(phrase)) {
                // 此處做了其他事情,違背了DO ONE THING!
                // 假如session已經存在,將導致不預期的結果
                Session.initialize();
                return true;
            }
        }
        return false;
    }
}

10、查詢和命令分離

// 如果存在attribute, 設置其值,返回true, 若不存在,返回false
public boolean set(String attribute, String value);

// 讀代碼的人一定蒙比
if (set("username", "unclebob")) ...

VS

// 這兒反而清晰的多
if (attributeExists("username")) { 
    setAttribute("username", "unclebob"); 
    ...
}

11、用異常,而不用返回錯誤碼


12、提取try/catch塊

try/catch 會導致代碼太醜了。理解和修改都不太方便。

// Do one thing.全是關於錯誤處理的
public void delete(Page page) {
    try {
        deletePageAndAllReferences(page);
    }
    catch (Exception e) {
        logError(e);
    }
}

// 第二抽象層,是delete的下屬抽象層,
// 注意已經沒有try/catch, 邏輯清晰
private void deletePageAndAllReferences(Page page) throws Exception {     
    deletePage(page);
    registry.deleteReference(page.name);
    configKeys.deleteKey(page.name.makeKey());
}

// error 處理
private void logError(Exception e) {
    logger.log(e.getMessage());
}

函數要只做一件事,而且錯誤處理是一件事,所以catch finally之後應該沒有其他的東西了!!!


13、不要使用相似的函數名!別產生confusion!


總結

  • Do one thing!
  • Do it well!
  • Do it only!
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章