由于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直接返回,通过上面的优化,代码的可读性得到了很大的提升。

我们还可以进一步的优化,可优化的点主要有:

  1. 利用接口,让buildWrapper变得可扩展,实现多种类型的Register注册
  2. 利用结构体,将全局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
}