Go 減少代碼的嵌套層數

軟件開發中的 “心智模型” 用於描述開發人員在編碼時心理活動,每段代碼其實是人在編寫這段代碼時的心智模型投射,不能把代碼看成是客觀的存在,而是主觀的產物,參合了當時心理活動或各種直覺感知。在編程時,必須不斷維護心智模型,例如關於整體代碼交互和功能實現的心智模型,代碼是否具有可讀性,代碼風格,變量命名規範等。可讀性強的代碼需要的心智模型更簡單,更容易閱讀和維護。

代碼嵌套層級的數量是影響可讀性的一個關鍵因素,假設正在一個新項目上進行開發工作,並且需要讀懂下面 join 函數實現的功能。此 join 函數實現的功能是將兩個字符連接起來,如果連接起來的字符串超過給定的長度 max,則返回前 max 個字符的子串。在處理的過程中,對參數進行檢查並判斷 concatenate 是否有返回錯誤。

func join(s1, s2 string, max int) (string, error) {
        if s1 == "" {
                return "", errors.New("s1 is empty")
        } else {
                if s2 == "" {
                        return "", errors.New("s2 is empty")
                } else {
                        concat, err := concatenate(s1, s2)
                        if err != nil {
                                return "", err
                        } else {
                                if len(concat) > max {
                                        return concat[:max], nil
                                } else {
                                        return concat, nil
                                }
                        }
                }
        }
}

func concatenate(s1 string, s2 string) (string, error) {
        // ...
}

從功能實現的角度來看,上述函數是正確的。然而,建立一個涵蓋所有不同輸入參數情況的心智模型可能不是一項簡單的任務。爲啥呢?由於代碼嵌套的數量層級過多。現在,對上面的代碼進行重構,得到新的實現如下。

func join(s1, s2 string, max int) (string, error) {
        if s1 == "" {
                return "", errors.New("s1 is empty")
        }
        if s2 == "" {
                return "", errors.New("s2 is empty")
        }
        concat, err := concatenate(s1, s2)
        if err != nil {
                return "", err
        }
        if len(concat) > max {
                return concat[:max], nil
        }
        return concat, nil
}

func concatenate(s1 string, s2 string) (string, error) {
        // ...
}

對比這兩種實現,雖然功能一樣,但是閱讀這個新版本的心智模型需要的認知負荷更少,因爲新版實現中函數嵌套層級更少。正如 Mat Ryer(Go Time 播客小組成員)所說:

將正常情況邏輯(happy path) 向左對齊,閱讀時能夠快速向下掃描一列可以查看預期的執行流程。

由於存在嵌套的 if/else 語句,很難看出重構前版本中的預期執行流程。相反,重構後的版本眼睛只需向下掃描一列便可知道預期的執行流程,通過第二列了解各種特殊情況的處理邏輯。

一般來說,函數需要的嵌套層數越多,閱讀和理解就越複雜。下面來看看如何使用這條規則優化代碼的可讀性。

if foo() {
        // ...
        return true
} else {
        // ...
}

而應該像下面這樣寫,將 else 語句省略掉,並將 else 語句塊中的邏輯移動到頂層,使其更易於閱讀。

if foo() {
        return true
}
// ...
if s != "" {
        // ...
} else {
        return errors.New("empty string")
}

上面 s 爲空是一個 non-happy 路徑,我們可以通過翻轉條件,得到如下實現。下面這種實現就更容易閱讀,因爲它將快樂路徑放在了左邊並減少了代碼塊的數量。

if s == "" {
        return errors.New("empty string")
}
// ...

編寫可讀的代碼對每個開發人員來說都是一項重要的挑戰,努力減少代碼塊嵌套的層級數量,將快樂路徑對齊放在左側,並儘早返回是提高代碼可讀性的具體手段,在工作中,我們應該應用這些手段。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/04h_yCgiAyrU_oQ-kcsYbg