理解Go流程控制與快樂路徑原則
有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
一、流程控制基本介紹
流程控制是每種編程語言控制邏輯走向和執(zhí)行次序的重要部分,流程控制可以說是一門語言的“經(jīng)脈”。
那么 Go 語言對(duì)分支與循環(huán)兩種控制結(jié)構(gòu)的支持是怎么樣的呢?針對(duì)程序的分支結(jié)構(gòu),Go 提供了 if 和 switch-case 兩種語句形式;我們就先從 Go 語言分支結(jié)構(gòu)之一的 if 語句開始講起。
二、if 語句
2.1 if 語句介紹
if 語句是 Go 語言中提供的一種分支控制結(jié)構(gòu),它也是 Go 中最常用、最簡(jiǎn)單的分支控制結(jié)構(gòu)。它會(huì)根據(jù)布爾表達(dá)式的值,在兩個(gè)分支中選擇一個(gè)執(zhí)行。
2.2 單分支結(jié)構(gòu)的 if 語句形式
單分支結(jié)構(gòu)的if語句包含一個(gè)條件表達(dá)式和一個(gè)要執(zhí)行的代碼塊。如果條件表達(dá)式的值為true,則執(zhí)行代碼塊。如果條件表達(dá)式的值為false,則代碼塊將被跳過。以下是單分支結(jié)構(gòu)的if語句的一般形式:
if boolean_expression {
// 新分支
}
// 原分支這個(gè) if 語句中的代碼執(zhí)行流程就等價(jià)于下面這幅流程圖:

boolean_expression是一個(gè)布爾表達(dá)式,通常返回true或false。- 如果
boolean_expression的值為true,則執(zhí)行// 當(dāng)條件為真時(shí)執(zhí)行的代碼部分的代碼塊。 - 如果
boolean_expression的值為false,則代碼塊將被跳過,繼續(xù)執(zhí)行下一個(gè)語句。
2.3 Go 的 if 語句的特點(diǎn)
2.3.1 分支代碼塊左大括號(hào)與if同行
if 語句的分支代碼塊的左大括號(hào)與 if 關(guān)鍵字在同一行上,這是 Go 代碼風(fēng)格的統(tǒng)一要求,gofmt 工具會(huì)幫助我們實(shí)現(xiàn)這一點(diǎn);
2.3.2 條件表達(dá)式不需要括號(hào)
if 語句的布爾表達(dá)式整體不需要用括號(hào)包裹,這使得代碼更加簡(jiǎn)潔。而且,if 關(guān)鍵字后面的條件判斷表達(dá)式的求值結(jié)果必須是布爾類型,即要么是 true,要么是 false:
if runtime.GOOS == "darwin" {
println("we are on MacOS")
}如果判斷的條件比較多,我們可以用多個(gè)邏輯操作符連接起多個(gè)條件判斷表達(dá)式,比如這段代碼就是用了多個(gè)邏輯操作符 && 來連接多個(gè)布爾表達(dá)式:
if (runtime.GOOS == "darwin") && (runtime.GOARCH == "amd64") &&
(runtime.Compiler != "gccgo") {
println("we are using standard go compiler on Mac os for amd64")
}上面示例代碼中的每個(gè)布爾表達(dá)式都被小括號(hào)括上了,這是為了降低你在閱讀和理解這段代碼時(shí),面對(duì)操作符優(yōu)先級(jí)的心智負(fù)擔(dān)。
三、操作符
3.1 邏輯操作符
邏輯操作符除了上面的 && 之外,Go 還提供了另外兩個(gè)邏輯操作符,如下表:

3.2 操作符的優(yōu)先級(jí)
一元操作符,比如上面的邏輯非操作符,具有最高優(yōu)先級(jí),其他操作符的優(yōu)先級(jí)如下:
| 優(yōu)先級(jí)(從高到低) | 操作符列表 |
|---|---|
| 5 | *, /, %, <<, >>, &, &^ |
| 4 | +, - |
| 3 | !=, ==, <, <=, >, >= |
| 2 | && |
| 1 | \ |
- 優(yōu)先級(jí)5的是乘、除、取模和位操作符
- 優(yōu)先級(jí)4的是加法和減法運(yùn)算符
- 優(yōu)先級(jí)3的是關(guān)系和相等運(yùn)算符
- 優(yōu)先級(jí)2的是邏輯與
- 優(yōu)先級(jí)最低的是邏輯或
操作符優(yōu)先級(jí)決定了操作數(shù)優(yōu)先參與哪個(gè)操作符的求值運(yùn)算,我們以下面代碼中 if 語句的布爾表達(dá)式為例:
func main() {
a, b := false,true
if a && b != true {
println("(a && b) != true")
return
}
println("a && (b != true) == false")
}這段代碼會(huì)輸出得到的是 a && (b != true) == false。這是為什么呢?
這段代碼的關(guān)鍵就在于,if 后面的布爾表達(dá)式中的操作數(shù) b 是先參與 && 的求值運(yùn)算,還是先參與!= 的求值運(yùn)算。根據(jù)前面的操作符優(yōu)先級(jí)表,我們知道,!= 的優(yōu)先級(jí)要高于 &&,因此操作數(shù) b 先參與的是!= 的求值運(yùn)算,這樣 if 后的布爾表達(dá)式就等價(jià)于 a && (b != true) 。
針對(duì)以上問題,推薦在 if 布爾表達(dá)式中,使用帶有小括號(hào)的子布爾表達(dá)式來清晰地表達(dá)判斷條件。
這樣做不僅可以消除了自己記住操作符優(yōu)先級(jí)的學(xué)習(xí)負(fù)擔(dān),當(dāng)其他人閱讀你的代碼時(shí),也可以很清晰地看出布爾表達(dá)式要表達(dá)的邏輯關(guān)系,這能讓我們代碼的可讀性更好,更易于理解,不會(huì)因記錯(cuò)操作符優(yōu)先級(jí)順序而產(chǎn)生錯(cuò)誤的理解。
三、if 多(N)分支結(jié)構(gòu)
3.1 if else(分支結(jié)構(gòu))
Go語言中if else(分支結(jié)構(gòu))條件判斷的格式如下:
if boolean_expression {
// 分支1
} else {
// 分支2
}3.2 (N)分支結(jié)構(gòu)(if ... else if ... else)
if條件(N)分支結(jié)構(gòu)格式如下:
if boolean_expression1 {
// 分支1
} else if boolean_expression2 {
// 分支2
... ...
} else if boolean_expressionN {
// 分支N
} else {
// 分支N+1
}我們以下面這個(gè)四分支的代碼為例,看看怎么拆解這個(gè)多分支結(jié)構(gòu):
if boolean_expression1 {
// 分支1
} else if boolean_expression2 {
// 分支2
} else if boolean_expression3 {
// 分支3
} else {
// 分支4
}以下是一個(gè)示例,演示如何使用if-else結(jié)構(gòu)來判斷一個(gè)分?jǐn)?shù)的等級(jí):
package main
import "fmt"
func main() {
score := 85
if score >= 90 {
fmt.Println("A")
} else if score >= 80 {
fmt.Println("B")
} else if score >= 70 {
fmt.Println("C")
} else {
fmt.Println("D")
}
}四、if 語句的自用變量
無論是單分支、二分支還是多分支結(jié)構(gòu),我們都可以在 if 后的布爾表達(dá)式前,進(jìn)行一些變量的聲明,在 if 布爾表達(dá)式前聲明的變量,叫 if 語句的自用變量。顧名思義,這些變量只可以在 if 語句的代碼塊范圍內(nèi)使用,比如下面代碼中的變量 a、b 和 c:
func main() {
if a, c := f(), h(); a > 0 {
println(a)
} else if b := f(); b > 0 {
println(a, b)
} else {
println(a, b, c)
}
}我們可以看到自用變量聲明的位置是在每個(gè) if 語句的后面,布爾表達(dá)式的前面,而且,由于聲明本身是一個(gè)語句,所以我們需要把它和后面的布爾表達(dá)式通過分號(hào)分隔開。
在 if 語句中聲明自用變量是 Go 語言的一個(gè)慣用法,這種使用方式直觀上可以讓開發(fā)者有一種代碼行數(shù)減少的感覺,提高可讀性。同時(shí),由于這些變量是 if 語句自用變量,它的作用域僅限于 if 語句的各層隱式代碼塊中,if 語句外部無法訪問和更改這些變量,這就讓這些變量具有一定隔離性,這樣你在閱讀和理解 if 語句的代碼時(shí)也可以更聚焦。
五、if 語句的“快樂路徑”原則
上面我們已經(jīng)學(xué)了 if 分支控制結(jié)構(gòu)的三種形式了,從可讀性上來看,單分支結(jié)構(gòu)要優(yōu)于二分支結(jié)構(gòu),二分支結(jié)構(gòu)又優(yōu)于多分支結(jié)構(gòu)。那么顯然,我們?cè)谌粘>幋a中要減少多分支結(jié)構(gòu),甚至是二分支結(jié)構(gòu)的使用,這會(huì)有助于我們編寫出優(yōu)雅、簡(jiǎn)潔、易讀易維護(hù)且不易錯(cuò)的代碼。
首先,我們來看一段偽代碼段1:
//偽代碼段1:
func doSomething() error {
if errorCondition1 {
// some error logic
... ...
return err1
}
// some success logic
... ...
if errorCondition2 {
// some error logic
... ...
return err2
}
// some success logic
... ...
return nil
}我們看到單分支控制結(jié)構(gòu)的偽代碼段 1 有這幾個(gè)特點(diǎn):
- 沒有使用 else 分支,失敗就立即返回;
- “成功”邏輯始終“居左”并延續(xù)到函數(shù)結(jié)尾,沒有被嵌入到 if 的布爾表達(dá)式為 true 的代碼分支中;
- 整個(gè)代碼段布局扁平,沒有深度的縮進(jìn);
- 代碼的可讀性很高
我們來看一段偽代碼段2:
// 偽代碼段2:
func doSomething() error {
if successCondition1 {
// some success logic
... ...
if successCondition2 {
// some success logic
... ...
return nil
} else {
// some error logic
... ...
return err2
}
} else {
// some error logic
... ...
return err1
}
}偽代碼段 2 實(shí)現(xiàn)了同樣邏輯碼段 1,就使用了帶有嵌套的二分支結(jié)構(gòu),它的特點(diǎn)如下:
- 整個(gè)代碼段呈現(xiàn)為“鋸齒狀”,有深度縮進(jìn);
- “成功”邏輯被嵌入到
if的布爾表達(dá)式為true的代碼分支中;
很明顯,偽代碼段 1 的邏輯更容易理解,也更簡(jiǎn)潔。Go 社區(qū)把這種 if 語句的使用方式稱為 if 語句的“快樂路徑(Happy Path)”原則,所謂“快樂路徑”也就是成功邏輯的代碼執(zhí)行路徑,它的特點(diǎn)是這樣的:
- 僅使用單分支控制結(jié)構(gòu);
- 當(dāng)布爾表達(dá)式求值為
false時(shí),也就是出現(xiàn)錯(cuò)誤時(shí),在單分支中快速返回; - 正常邏輯在代碼布局上始終“靠左”,這樣讀者可以從上到下一眼看到該函數(shù)正常邏輯的全貌;
- 函數(shù)執(zhí)行到最后一行代表一種成功狀態(tài)。
Go 社區(qū)推薦 Gopher 們?cè)谑褂?if 語句時(shí)盡量符合這些原則,如果你的函數(shù)實(shí)現(xiàn)代碼不符合“快樂路徑”原則,你可以按下面步驟進(jìn)行重構(gòu):
- 嘗試將“正常邏輯”提取出來,放到“快樂路徑”中;
- 如果無法做到上一點(diǎn),很可能是函數(shù)內(nèi)的邏輯過
以上就是Go流程控制與快樂路徑原則的詳細(xì)內(nèi)容,更多關(guān)于Go流程控制路徑原則的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
關(guān)于Golang變量初始化/類型推斷/短聲明的問題
這篇文章主要介紹了關(guān)于Golang變量初始化/類型推斷/短聲明的問題,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-02-02
Golang時(shí)間處理庫(kù)go-carbon?v2.2.13發(fā)布細(xì)則
這篇文章主要為大家介紹了Golang?時(shí)間處理庫(kù)go-carbon?v2.2.13發(fā)布細(xì)則,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-11-11

