1. 盡早返回
反例:
//UserCtrl
func UserInfo(userId string){
user.UserInfo(userId)
....
....
//resp result ...
}
//UserService
func UserInfo(userId string){
if len(userId) > 0 {
//do query database
.....
}
}
// repo
func queryUserInfo(userId string){
if len(userId) > 0{
//select * from user where user_id = ?
}
}
從這個(gè)例子來(lái)看,在service層和數(shù)據(jù)庫(kù)查詢,我們都進(jìn)行了userId的判斷. 因?yàn)楫?dāng)我們經(jīng)常會(huì)忘記,我們是否在上一層入?yún)⒌臅r(shí)候進(jìn)行了userId為空的判斷. 為了避免空指針,我們不得已一層層進(jìn)行判斷.
假如我們盡早地返回,那么就可以避免后續(xù)的層層判空
推薦寫(xiě)法:
//userCtrl
func UserInfo(userId string){
if len(userId) == 0 {
//resp some error
}
user.UserInfo(userId)
}
2. 寫(xiě)好分支語(yǔ)句
反例:
func xxx(lang string){
if lang == "java"{
doA()
} else {
doB()
}
if lang == "java"{
doC()
}else{
doD()
}
}
推薦寫(xiě)法:
func java(lang string){
doA()
doC()
}
func other(lang string){
doB()
doC()
}
if都需要包含else
反例:
if cmd == "1" {
if status == 0 {
doSome()
}
}else {
if tag == 1 {
doSomeB()
}
}
推薦寫(xiě)法:
if cmd == "1" {
if status == 0 {
}else{
}
}else {
if tag == 1{
}else {
}
}
避免啰嗦的條件
if isDone() == true {
do()
}
推薦:
if isDone() {
doSome()
}
使用switch語(yǔ)句
反例:
if lang == "java"{
}else if lang == "c#" {
}else if lang == "clojure"{
}
推薦寫(xiě)法:
switch lang:
case "java":
case "c#":
case "clojure":
減少邏輯表達(dá)式:
邏輯表達(dá)式是門(mén)電路的表達(dá)式,總會(huì)有人不能記得他的先后執(zhí)行順序。
反例:
if lang == "java" || lang == "c#" && lang == "clojure" {
doSome()
}
如果非得使用邏輯表達(dá)式,推薦使用括號(hào),顯示地說(shuō)明調(diào)用的順序.
if (lang == "java" || lang == "c#") && lang == "clojure" {
doSome()
}
使用正序的邏輯
反例:
if !isUserInfoSaved() {
doA()
}else {
doB()
}
if !isNotStop() {
doSome()
}
用一個(gè)符合人類思考順序方式來(lái)寫(xiě)分支,減少閱讀代碼時(shí)的時(shí)間
if isUserInfoSaved() {
doB()
}else {
doA()
}
if isStart(){
doSome()
}
3.合理使用局部變量
可能我們知道定義一個(gè)變量會(huì)開(kāi)辟一塊新的內(nèi)存,有時(shí)候覺(jué)得自己重復(fù)使用一個(gè)變量,會(huì)讓性能"好一些", 于是我們就會(huì)寫(xiě)出下面的代碼
反例:
var name
if login {
name = "user"
doSome(name)
}else {
name = "guest"
doSome(name)
}
其實(shí)在棧上開(kāi)辟內(nèi)存的成本很低,編譯器會(huì)對(duì)代碼進(jìn)行逃逸分析,而且執(zhí)行完這個(gè)方法后,內(nèi)存就會(huì)被回收掉,所以不用擔(dān)心這個(gè)性能問(wèn)題.
推薦寫(xiě)法:
if login {
//使用局部變量
name := "user"
doSome(name)
}else {
name := "guest"
doSome(name)
}
有的時(shí)候我們會(huì)想耍個(gè)酷,那么牛逼的調(diào)用一行代碼就寫(xiě)完了,可是這時(shí)候閱讀起來(lái)是非常痛苦的一件事情.
反例:
saveXXX(queryRole(),queryOrder(),saveXXX(queryUserInfo(genUserId())))
推薦寫(xiě)法:
我們把參數(shù)通過(guò)一個(gè)中間變量存起來(lái),這樣會(huì)很明顯地說(shuō)明,我們都干了些什么.
userInfo := queryUserInfo(genUserId)
u := saveXXX(userInfo)
saveXXX(queryRole(),queryOrder(),u)
4.循環(huán)
循環(huán)本身就不好讀,假如在循環(huán)中包含continue,break之類的,讓原本的代碼更難讀
反例:
for i,itm := range Users {
if item.Name != "admin" {
continue
}else {
doSome()
}
}
推薦寫(xiě)法:
for i,itm := range Users {
if item.Name == "admin" {
doSome()
}
}
使用i,j之類的下標(biāo),本身就比較相似,一不小心就會(huì)造成了下標(biāo)越界,推薦使用foreach
反例:
for i:=0 ; i< len(users); i++ {
for j:=0 ; j < len(users[i].children); j++ {
// doSome
}
}
推薦寫(xiě)法
for _,itm := range users {
for _, c := range item{
//dosome
}
}
假如非得使用下標(biāo)操作,也要避免使用i,j之類的變量
for uidx :=0 ; uidx< len(users); uidx++ {
childen := users[uidx].children //使用中間變量,避免臃腫
for chidx:=0 ; chidx < len(childen); chidx++ {
// chidx 和 uidx 能避免混淆,能在使用的過(guò)程中避免出錯(cuò)。
}
}
5.面條代碼, 過(guò)程式代碼
面條代碼過(guò)程式代碼
type UserInf struct {
UserName string
UserId string
Role *Role
Alias string
}
type Role struct {
Id string
Name string
}
func save(inf *UserInf){
Role(inf)
inf.Alias = genAlias()
update(inf)
}
func update(inf *UserInf){
//update ....
}
func Role(inf *UserInfo) {
queryUser(inf)
inf.Role = queryRole(inf.UserId)
}
func queryUser(inf *UserInf){
//select * from user where user_id = ?
inf.UserName = ...
inf.xxx = ...
}
思考: 為什么要使用純函數(shù)?
6. 控制代碼的長(zhǎng)度
人的左腦關(guān)心的是邏輯,右腦做的是快照(可能是偽科學(xué)),實(shí)際情況中,假如我們一眼能看完,是不是剩下的就是在想邏輯,而不是一邊讀代碼,一邊想邏輯,這樣能讓我們大腦一次性把看到的代碼緩存起來(lái),然后專注于想邏輯。簡(jiǎn)短的代碼,也可以避免bug,所以方法建議都控制在100行之內(nèi)。
7. 命名
這個(gè)放最后來(lái)寫(xiě)的原因是這個(gè)命名本來(lái)就很難,命名得好就會(huì)讓代碼清晰可讀,命名不好,就會(huì)誤導(dǎo)導(dǎo)致需要大量的注視來(lái)注釋代碼,本來(lái)維護(hù)代碼已經(jīng)是一件痛苦的事情了,假如在修改了代碼后,注釋沒(méi)有同步修改,反而會(huì)引起誤導(dǎo)。因?yàn)槟刚Z(yǔ)不是英文,很多同學(xué)跟我一樣也都很痛苦,這里可以上網(wǎng)找找相關(guān)命名的資料,本人水平有限也只能是大概地舉幾個(gè)例子
bool isStart // 服務(wù)啟動(dòng)的狀態(tài),最好使用正向的表達(dá),是否啟動(dòng),不推薦使用 bool isNotStop
func ComputeUserScore() //計(jì)算用戶積分,假如這是一個(gè)耗時(shí)的操作,推薦在方法名上就表示出來(lái),不推薦使用GetUserScore,
func DownloadFile() //下載文件,不推薦使用GetFile
個(gè)人博客 https://youkale.github.io