由于Go语言对错误的设计方式,导致程序中可能出现大量的 if err !=nil{return err } return nil
有时候明明两行代码就能解决的事,由于error,我们可能需要10行
Go语言中的错误设计方式有好有坏,我们不讨论如何避免错误的产生,只讨论如何更优雅的处理error,需要注意的是,程序中的每一个错误我们都应该处理,不应选择忽视。
接下来我们看一段糟糕的代码:
type Register struct {}
func (Register) Translation() error {
return nil
}
func (Register) Validation() error {
return nil
}
func Build() error {
// 创建注册器1
r1 := Register{}
err := r1.Validation()
if err != nil {
return err
}
err = r1.Translation()
if err != nil {
return err
}
// 创建注册器2
r2 := Register{}
err = r2.Validation()
if err != nil {
return err
}
err = r2.Translation()
if err != nil {
return err
}
// r3 r4 r5 ......错误处理将会越来越多
return nil
}
分析:通过程序我们知道,这是一段很糟糕的程序,如果Register因为需求而不断增加,程序中的if err !=nil{return err } return nil 会越来越多,这让我们感动很头疼。接下来我们将对此进行优化。
我们发现程序中存在许多重复代码段,r1.Translation()和r1.Validation()的调用和错误检查,因此,我们考虑将它剥离为一个方法。
type Register struct {}
func (Register) Translation() error {
return nil
}
func (Register) Validation() error {
return nil
}
func Build() error {
// 创建注册器1
r1 := Register{}
err := buildWrapper(r1)
if err != nil {
return err
}
// 创建注册器2
r2 := Register{}
err = buildWrapper(r2)
if err != nil {
return err
}
// r3 r4 r5 ......错误处理将会越来越多
return nil
}
func buildWrapper(r Register) error {
err := r.Validation()
if err != nil {
return err
}
err = r.Translation()
if err != nil {
return err
}
return nil
}
通过剥离出buildWrapper,可以看到我们的程序稍微好点了,但是,我们还是避免不了要处理buildWraper返回的error
我们再对程序进行优化。
type Register struct {}
func (Register) Translation() error {
return nil
}
func (Register) Validation() error {
return nil
}
func Build() error {
// 函数内存全局
var err error
// 闭包
errWrapper := func(r Register) {
if err != nil {
return
}
err = buildWrapper(r)
}
// 创建注册器1
r1 := Register{}
errWrapper(r1)
// 创建注册器2
r2 := Register{}
errWrapper(r2)
// 创建注册器3
r3 := Register{}
errWrapper(r3)
// 创建注册器4
r4 := Register{}
errWrapper(r4)
if err != nil {
return err
}
return nil
}
func buildWrapper(r Register) error {
err := r.Validation()
if err != nil {
return err
}
err = r.Translation()
if err != nil {
return err
}
return nil
}
我们创建一个全局的error,并创建一个闭包errWrapper,如果发生err,则不执行buildWrapper直接返回,通过上面的优化,代码的可读性得到了很大的提升。
我们还可以进一步的优化,可优化的点主要有:
- 利用接口,让buildWrapper变得可扩展,实现多种类型的Register注册
- 利用结构体,将全局error和闭包放到结构体中
最后,我们就可以得到我们想要的结果
type BaseRegister struct {}
func (BaseRegister) Translation() error {
return nil
}
func (BaseRegister) Validation() error {
return nil
}
type Register interface {
Translation() error
Validation() error
}
type ErrorWrapper struct {
err error
}
func (ew *ErrorWrapper) wrapper(r Register) {
if ew.err != nil {
return
}
ew.err = buildWrapper(r)
}
func Build() error {
// 闭包
errWrapper := &ErrorWrapper{}
// 创建注册器1
r1 := BaseRegister{}
errWrapper.wrapper(r1)
// 创建注册器2
r2 := BaseRegister{}
errWrapper.wrapper(r2)
// 创建注册器3
r3 := BaseRegister{}
errWrapper.wrapper(r3)
// 创建注册器4
r4 := BaseRegister{}
errWrapper.wrapper(r4)
if errWrapper.err != nil {
return errWrapper.err
}
return nil
}
func buildWrapper(r Register) error {
err := r.Validation()
if err != nil {
return err
}
err = r.Translation()
if err != nil {
return err
}
return nil
}