順序執行模組程式碼處理技巧——error轉panic
阿新 • • 發佈:2018-12-11
問題描述
完成某項具體工作時, 通常會將工作拆分成多個子模組, 然後將各個子模組進行有效組合即可解決問題. 當各個子模組具有嚴格依賴關係時, 通常對每一個子模組的執行結果進行判斷, 程式碼結構如下:
func processBigProgram() (da interface{}, err error) { data, err := f1() if err != nil { return data, err } data, err = f2() if err != nil { return data, err } data, err = f3() if err != nil { return data, err } data, err = f4() if err != nil { return data, err } return }
不難看出完成該工作只需要依次執行f1、f2、f3、f4. 但是每個函式(子模組)執行完成之後都對err進行判斷, 使得簡單的主線被掩藏. 程式碼行數被放大了4倍. 當然也可以將判斷與函式執行放到一行, 但是並沒有解決任何問題.
嘗試解決
各個子模組執行完成之後需要判定執行過程中是否發生err, 若err!=nil, 返回相應結果. 程式碼功能完全相同, 因此可以將該部分程式碼提取為函式proxyFunc, 當proxyFunc執行有err時通過傳送panic, 中斷程式繼續向下執行. 最後通過defer接收panic, 解析最終返回結果. 完整實現程式碼如下. 通過proxyFunc處理之後, test()主脈絡更清晰, 程式碼更容易閱讀, 也更整潔.
package main import ( "encoding/json" "fmt" "github.com/pkg/errors" "math/rand" "os" "time" ) type result struct { data interface{} errV error } type Music struct { name string id int } func main() { res := test() if res == nil { fmt.Println("result is nil") os.Exit(1) } data, ok := res.(Music) if !ok { fmt.Println("result is not music") os.Exit(1) } fmt.Println(data) } func test() (data interface{}) { defer func() { re := recover() r, ok := re.(result) if ok { b, _ := json.Marshal(r) fmt.Println("system error,data:", string(b)) data = r } else { fmt.Println("other panic process...") } }() var music = Music{name: "origin", id: 0} music = proxyFunc(test1, validateTrue, music).(Music) music = proxyFunc(test2, validateTrue, music).(Music) music = proxyFunc(test3, validateTrue, music).(Music) music = proxyFunc(test4, validateTrue, music).(Music) fmt.Println("run success!!!!!") return music } func proxyFunc(f func(param ...interface{}) (data interface{}, err error), validate func(data interface{}, err error), param ...interface{}) (res interface{}) { data, err := f(param) validate(data, err) return data } func validateTrue(data interface{}, err error) { if err != nil { panic(result{data: data, errV: err}) } } func test1(param ...interface{}) (data interface{}, err error) { rand.Seed(time.Now().UnixNano()) var music = Music{name: "test1", id: 1} if rand.Int31n(12)%3 == 0 { err = errors.New("test1 error") fmt.Println("test1 error") return data, err } fmt.Println("test1 success") return music, nil } func test2(param ...interface{}) (data interface{}, err error) { rand.Seed(time.Now().UnixNano()) var music = Music{name: "test2", id: 2} if rand.Int31n(12)%3 == 0 { err = errors.New("test3 error") fmt.Println("test2 error") return data, err } fmt.Println("test2 success") return music, nil } func test3(param ...interface{}) (data interface{}, err error) { rand.Seed(time.Now().UnixNano()) var music = Music{name: "test3", id: 3} if rand.Int31n(12)%3 == 0 { err = errors.New("test3 error") fmt.Println("test3 error") return data, err } fmt.Println("test3 success") return music, nil } func test4(param ...interface{}) (data interface{}, err error) { rand.Seed(time.Now().UnixNano()) var music = Music{name: "test4", id: 4} if rand.Int31n(12)%3 == 0 { err = errors.New("test4 error") fmt.Println("test4 error") return data, err } fmt.Println("test4 success") return music, nil }