背景介绍
通过前面两讲,我们对错误的认知已经超过很多人了。让我们继续去看看常见项目中对错误的处理方式,探索背后的深意。
在介绍具体的处理方式前,我们先来模拟一个场景:我们要去动物园进行一次游玩,主要行为有
- 进入动物园
- 参观熊猫
- 参观老虎
- 离开动物园
第一种风格 - 经典Go语言的处理模式
1 | // 一次旅游 |
这个处理风格非常经典。我们先不深入讨论,看完下一种后再做对比。
第二种风格 - 类似Try-Catch的代码风格
1 | type ZooTour2 interface { |
这一整块的代码风格非常类似Try Catch,即先写业务逻辑,在最后对错误进行集中处理。
标准库中的
bufio.Scanner
就是参考这种方式实现的。
不过,由于Go语言对error的处理没有往外抛的机制,所以需要专门针对error做处理:
新手千万不要把panic的机制和错误处理混为一谈。
1 | // ZooTour的具体实现,需要保存一个error |
两种风格的对比
如果分别用一个词来形容前两种风格,我倾向于:
- 过程式的调用
- 集中处理错误
两种风格无法说清孰优孰劣,但有各自适宜的场景,我们来列举两种:
不关注错误的发生,而关注错误发生后的统一处理
内部存在大量的VisitXXX
的函数,业务不关注发生错误的处理逻辑,而是关注整个流程完成后对error的处理。
例如,调用过程中如果出现了某个动物不在的问题,我们不关心,继续访问下一个,最后统一处理一下,看看有多少动物是不在的,打印一下即可。
这时,第二种处理方式明显会更简洁。
一般推荐在工具类中采用这种方式,处理的内容比较直观,不会有太多异常case
错误有多种分类,会影响到程序的运行逻辑
例如VisitPanda(panda *Panda)
可能产生的错误分2类:
不影响主流程:例如发现panda不见了,但还要接着继续参观其余动物
影响主流程:例如突然收到动物园闭园的通知,不能参观其余动物了
这时,如果我们采用第二种风格,就得在每个函数内部加上很多特殊的业务逻辑:
1 | func (t *myZooTour) VisitTiger(tiger *Tiger) { |
很有可能出现一个问题:把Panda相关的error放到了Tiger里。
所以,当错误的类型会影响到代码的运行逻辑,更适合第一种方案。
一般情况下,我们的业务代码都是复杂的,这时候更适合写过程性的代码。
第三种风格 - 函数式编程
借用1中的接口定义,我们将它改造成函数式的风格:
1 | type MyFunc func(t ZooTour1) error |
然后调用代码示例如下:
1 | func Tour3(t ZooTour1, panda *Panda, tiger *Tiger) error { |
值得一提的是
ContinueOnError
表示遇到了error只记录下来,但整个流程继续往下跑BreakOnError
表示遇到了error就直接break,不再跑接下来的MyFunc
方案三背后的思想与延伸
函数式编程最直观的一个特点是 延迟执行,也就是在引用MyFunc
处不运行,在ContinueOnError
或BreakOnError
里才是真正执行的地方。
这个延迟执行的特性,在这里还能达到一个很有意思的效果 - 分离关注点。
关注点1 - 数据结构
样例中的[]MyFunc
是一个切片,可以简单地理解为串行执行,也就是MyFunc
执行完一个,再执行下一个。
我们可以引入更多的数据结构,例如[][]MyFunc
,那就可以理解为增加了一层:
每一层中的[]MyFunc
,代表这里面的所有MyFunc
是平级的,也就可以采用一定的并发模式来加速执行。
关注点2 - 执行逻辑
以ContinueOnError
或BreakOnError
为例,它们都是对各种MyFunc
的处理逻辑。我们还可以引入更多的执行逻辑,比如:
- 容忍特定错误的情况
- 对错误发生的数量有容忍上限
- 保证一定的并发模式
流水线的模式
以我们常见的开发流水线为例,常见的包括:代码检查、单元测试、编译、CodeReview、自动化部署等。
这时,数据结构可以用来表示流水线的结构,执行逻辑可以用来表示流水线对异常的处置。
比如说,我们可以编排为一种串行执行的逻辑:
- 代码检查
- 单元测试
- 编译
- CodeReview
- 自动化部署
我们想要加速整个流程,可以考虑修改为:
- 检查
- 代码检查
- 单元测试
- 编译
- CodeReview
- 自动化部署
结束语
本文介绍了三种对error的处理方式,代码实现相对简单,大家更需要关注背后的适用场景。
其中,第三种方式是一个很有意思的设计模式,可以帮助大家理解函数式编程的价值。
Github: https://github.com/Junedayday/code_reading
Blog: http://junes.tech/
Bilibili: https://space.bilibili.com/293775192
公众号: golangcoding