问题描述
完成某项具体工作时, 通常会将工作拆分成多个子模块, 然后将各个子模块进行有效组合即可解决问题. 当各个子模块具有严格依赖关系时, 通常对每一个子模块的运行结果进行判断, 代码结构如下:
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"
"errors"
"fmt"
"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)
}
if data, ok := res.(Music); !ok {
fmt.Println("result is not music")
os.Exit(1)
} else {
fmt.Println(data)
}
}
func test() (data interface{}) {
defer func() {
if re := recover(); re != nil {
if r, ok := re.(result); 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
}