1. 程式人生 > >golang如何優雅的編寫事務程式碼

golang如何優雅的編寫事務程式碼

[toc] # 前言 ![](https://img2020.cnblogs.com/blog/662544/202005/662544-20200525173257663-851975192.png) 新手程式設計師大概有如下特點 1. if巢狀經常超過3層、經常出現重複程式碼、單個函式程式碼特別長。 2. 只會crud,對語言特性和語言的邊界不瞭解。 3. 不懂面向物件原則和設計模式,以為copy程式碼就算學會了,常見的是程式碼職責不明確或者寫出萬能類 4. 不知道資料結構和演算法的重要性,以為靠硬體能解決執行慢的問題 5. 架構不懂,搭建框架不會,搭建環境不會,使用的軟體底層原理一問三不知 其實吧,很多人幹了很多年,看似是老手,平時工作看似很忙,其實做的都是最簡單的活。 這就像去鍛鍊,有的人每天練的很積極,準時打卡,頻繁發朋友圈,貌似是正能量,結果是幾年下來體型還是那樣,該減的肥肉沒少,要增的肌肉沒加,為什麼會這樣?因為從來都是挑最簡單最輕鬆的練 貌似吐槽多了,下面演示一下如何將一坨爛事務程式碼重構得優雅 # 需求 執行一個事務,需要呼叫one、two、three、four、five幾個方法,任意一個方法失敗,都回滾事務 下面是這些方法的簡單模擬,我們用盡可能少的程式碼模擬一個操作 ``` //開啟事務 func beginTransaction() { fmt.Println("beginTransaction") } //回滾事務 func rollback() { fmt.Println("rollback") } //提交事務 func commit() { fmt.Println("commit") } //執行one操作 func one() (err error) { fmt.Println("one ok") return nil } //執行two操作 func two() (err error) { fmt.Println("two ok") return nil } //執行three操作 func three() (err error) { fmt.Println("two ok") return nil } //執行four操作 func four() (err error) { fmt.Println("four ok") return nil } //執行five操作 func five() (err error) { err = errors.New("five panic") panic("five") return err } ``` # 爛程式碼示例 下面演示開啟一個事務,依次執行one、two、three、four、five 5個操作,前四個成功,第五個失敗 這是新手程式設計師常見使用事務的程式碼風格,其實也不光是事務,所有的程式碼都可能長下邊這樣 ![](https://img2020.cnblogs.com/blog/662544/202005/662544-20200525171321824-1175571223.png) ``` func demo() (err error) { beginTransaction() defer func() { if e := recover(); e != nil { err = fmt.Errorf("%v", e) fmt.Printf("%v panic\n", e) rollback() } }() if err = one(); err == nil { if err = two(); err == nil { if err = three(); err == nil { if err = four(); err == nil { if err = five(); err == nil { commit() return nil } else { rollback() return err } } else { rollback() return err } } else { rollback() return err } } else { rollback() return err } } else { rollback() return err } } ``` # 重構套路 ## 一、提前return去除if巢狀 通過提前返回error,來去掉一些else程式碼,減少巢狀,如下 ![](https://img2020.cnblogs.com/blog/662544/202005/662544-20200525174755497-55433070.png) 程式碼 ``` func demo() (err error) { beginTransaction() defer func() { if e := recover(); e != nil { err = fmt.Errorf("%v", e) fmt.Printf("%v panic\n", e) rollback() } }() if err = one(); err != nil { rollback() return err } if err = two(); err != nil { rollback() return err } if err = three(); err != nil { rollback() return err } if err = four(); err != nil { rollback() return err } if err = five(); err != nil { rollback() return err } commit() return nil } ``` 先解決巢狀 ## 二、goto+label提取重複程式碼 ![](https://img2020.cnblogs.com/blog/662544/202005/662544-20200525174602597-623257484.png) 程式碼 ``` func demo() (err error) { beginTransaction() defer func() { if e := recover(); e != nil { err = fmt.Errorf("%v", e) fmt.Printf("%v panic\n", e) rollback() } }() if err = one(); err != nil { goto ROLLBACK } if err = two(); err != nil { goto ROLLBACK } if err = three(); err != nil { goto ROLLBACK } if err = four(); err != nil { goto ROLLBACK } if err = five(); err != nil { goto ROLLBACK } commit() return nil ROLLBACK: rollback() return err } ``` ## 三、封裝try-catch統一捕獲panic 上面的程式碼其實還有一點問題 1. defer裡有rollback的程式碼 2. goto雖然好,但是不建議使用 我們可以對panic和defer進行封裝,模擬一下try-catch,實現如下 ![](https://img2020.cnblogs.com/blog/662544/202005/662544-20200525172545976-4750387.png) ![](https://img2020.cnblogs.com/blog/662544/202005/662544-20200525172636804-1691579534.png) `try-catch.go`程式碼 ``` package exception type Block struct { Try func() Catch func(interface{}) Finally func() } func (t Block) Do() { if t.Finally != nil { defer t.Finally() } if t.Catch != nil { defer func() { if r := recover(); r != nil { t.Catch(r) } }() } t.Try() } ``` 使用程式碼 ``` func demo() (err error) { exception.Block{ Try: func() { beginTransaction() if err = one(); err != nil { panic(err) } if err = two(); err != nil { panic(err) } if err = three(); err != nil { panic(err) } if err = four(); err != nil { panic(err) } if err = five(); err != nil { panic(err) } err = nil commit() }, Catch: func(e interface{}) { err = fmt.Errorf("%v", e) fmt.Printf("%v panic\n", e) }, }.Do() return err } ``` 這樣,我們就可以用非常少的程式碼實現事務,並且簡單清晰好維護,以上為chenqionghe原創,light wei