golang如何优雅的编写事务代码
前言

新手程序员大概有如下特点
- if嵌套经常超过3层、经常出现重复代码、单个函数代码特别长。
- 只会crud,对语言特性和语言的边界不了解。
- 不懂面向对象原则和设计模式,以为copy代码就算学会了,常见的是代码职责不明确或者写出万能类
- 不知道数据结构和算法的重要性,以为靠硬件能解决运行慢的问题
- 架构不懂,搭建框架不会,搭建环境不会,使用的软件底层原理一问三不知
其实吧,很多人干了很多年,看似是老手,平时工作看似很忙,其实做的都是最简单的活。
这就像去锻炼,有的人每天练的很积极,准时打卡,频繁发朋友圈,貌似是正能量,结果是几年下来体型还是那样,该减的肥肉没少,要增的肌肉没加,为什么会这样?因为从来都是挑最简单最轻松的练
貌似吐槽多了,下面演示一下如何将一坨烂事务代码重构得优雅
需求
执行一个事务,需要调用one、two、three、four、five几个方法,任意一个方法失败,都回滚事务
下面是这些方法的简单模拟,我们用尽可能少的代码模拟一个操作
//开启事务
func beginTransaction() {
fmt.Println("beginTransaction")
}
//回滚事务
func rollback() {
fmt.Println("rollback")
}
//提交事务
func commit() {
fmt.Println("commit")
}
//执行one操作
func one() (err error) {
fmt.Println("one ok")
return nil
}
//执行two操作
func two() (err error) {
fmt.Println("two ok")
return nil
}
//执行three操作
func three() (err error) {
fmt.Println("two ok")
return nil
}
//执行four操作
func four() (err error) {
fmt.Println("four ok")
return nil
}
//执行five操作
func five() (err error) {
err = errors.New("five panic")
panic("five")
return err
}
烂代码示例
下面演示开启一个事务,依次执行one、two、three、four、five 5个操作,前四个成功,第五个失败
这是新手程序员常见使用事务的代码风格,其实也不光是事务,所有的代码都可能长下边这样

func demo() (err error) {
beginTransaction()
defer func() {
if e := recover(); e != nil {
err = fmt.Errorf("%v", e)
fmt.Printf("%v panic\n", e)
rollback()
}
}()
if err = one(); err == nil {
if err = two(); err == nil {
if err = three(); err == nil {
if err = four(); err == nil {
if err = five(); err == nil {
commit()
return nil
} else {
rollback()
return err
}
} else {
rollback()
return err
}
} else {
rollback()
return err
}
} else {
rollback()
return err
}
} else {
rollback()
return err
}
}
重构套路
一、提前return去除if嵌套
通过提前返回error,来去掉一些else代码,减少嵌套,如下

代码
func demo() (err error) {
beginTransaction()
defer func() {
if e := recover(); e != nil {
err = fmt.Errorf("%v", e)
fmt.Printf("%v panic\n", e)
rollback()
}
}()
if err = one(); err != nil {
rollback()
return err
}
if err = two(); err != nil {
rollback()
return err
}
if err = three(); err != nil {
rollback()
return err
}
if err = four(); err != nil {
rollback()
return err
}
if err = five(); err != nil {
rollback()
return err
}
commit()
return nil
}
先解决嵌套
二、goto+label提取重复代码

代码
func demo() (err error) {
beginTransaction()
defer func() {
if e := recover(); e != nil {
err = fmt.Errorf("%v", e)
fmt.Printf("%v panic\n", e)
rollback()
}
}()
if err = one(); err != nil {
goto ROLLBACK
}
if err = two(); err != nil {
goto ROLLBACK
}
if err = three(); err != nil {
goto ROLLBACK
}
if err = four(); err != nil {
goto ROLLBACK
}
if err = five(); err != nil {
goto ROLLBACK
}
commit()
return nil
ROLLBACK:
rollback()
return err
}
三、封装try-catch统一捕获panic
上面的代码其实还有一点问题
- defer里有rollback的代码
- goto虽然好,但是不建议使用
我们可以对panic和defer进行封装,模拟一下try-catch,实现如下


可以看到,rollback只调用了一次,完美的进行了事务代码重构
try-catch.go代码
package exception
type Block struct {
Try func()
Catch func(interface{})
Finally func()
}
func (t Block) Do() {
if t.Finally != nil {
defer t.Finally()
}
if t.Catch != nil {
defer func() {
if r := recover(); r != nil {
t.Catch(r)
}
}()
}
t.Try()
}
使用代码
exception.Block{
Try: func() {
beginTransaction()
if err = one(); err != nil {
panic(err)
}
if err = two(); err != nil {
panic(err)
}
if err = three(); err != nil {
panic(err)
}
if err = four(); err != nil {
panic(err)
}
if err = five(); err != nil {
panic(err)
}
err = nil
commit()
},
Catch: func(e interface{}) {
rollback()
fmt.Printf("%v panic\n", e)
err = fmt.Errorf("%v", e)
},
}.Do()
return err
}
这样,我们就可以用非常少的代码实现事务,并且简单清晰好维护,以上为chenqionghe原创,light weight baby
golang如何优雅的编写事务代码的更多相关文章
- .Net高级进阶,在复杂的业务逻辑下,如何以最简练的代码,最直观的编写事务代码?
本文将通过场景例子演示,来通俗易懂的讲解在复杂的业务逻辑下,如何以最简练的代码,最直观的编写事务代码. 通过一系列优化最终达到两个效果,1.通过代码块来控制事务(分布式事务),2.通过委托优化Tran ...
- C#和JAVA中编写事务代码
C# DAL层代码,运行多条增删改,使用事务操作: /// <summary> /// 运行 多条增删改 (非查询语句) /// </summary> /// <par ...
- (转).Net高级进阶,在复杂的业务逻辑下,如何以最简练的代码,最直观的编写事务代码?
原文地址:http://www.cnblogs.com/1996V/p/7798111.html 示例一和示例二,主要是来讲解 TransactionScope 是什么,为什么要用Transactio ...
- 使用lambda表达式优雅你的事务代码
我们在实现业务逻辑时,经常会有这种需求: 1.在当前事务A中开启新事务B,事务B中发生异常不可以回滚A,事务B成功执行后返回事务B的返回值: 2.在当前事务A中开启新事务B,事务B中发生异常要求回滚A ...
- 使用 Golang 编写链代码 (v0.6 )
https://www.ibm.com/developerworks/cn/cloud/library/cl-ibm-blockchain-chaincode-testing-using-golang ...
- 最新的JavaScript核心语言标准——ES6,彻底改变你编写JS代码的方式!【转载+整理】
原文地址 本文内容 ECMAScript 发生了什么变化? 新标准 版本号6 兑现承诺 迭代器和for-of循环 生成器 Generators 模板字符串 不定参数和默认参数 解构 Destructu ...
- 如果让莎士比亚、海明威编写JavaScript代码
本文作者Angus Croll是Twitter工程师.JavaScript迷.文学迷,并且非常喜欢作家海明威.他在梦中"梦见"一些名人编写JavaScript代码,不同的作家呈现出 ...
- Guava 教程1-使用 Google Collections,Guava,static imports 编写漂亮代码
原文出处: oschina (API:http://ifeve.com/category/framework/guava-2/ JAR DOC Source 链接:http://pan.baidu.c ...
- 如何更规范化编写Java 代码
如何更规范化编写Java 代码 Many of the happiest people are those who own the least. But are we really so happy ...
随机推荐
- mybatis源码学习(四):动态SQL的解析
之前的一片文章中我们已经了解了MappedStatement中有一个SqlSource字段,而SqlSource又有一个getBoundSql方法来获得BoundSql对象.而BoundSql中的sq ...
- 【ElasticSearch学习】之一图读懂文档索引全过程
ES索引过程详解: 1.客户端发送索引请求. 客户端向ES节点发送索引请求,以RestClient客户端发起请求为例: ES提供了Java High Level REST Client,用户可以通过R ...
- 洛谷P3360偷天换日(树形DP)
题目背景 神偷对艺术馆内的名画垂涎欲滴准备大捞一把. 题目描述 艺术馆由若干个展览厅和若干条走廊组成.每一条走廊的尽头不是通向一个展览厅,就 是分为两个走廊.每个展览厅内都有若干幅画,每副画都有一个价 ...
- python(os 模块)
1.os.name 输出字符串指示正在使用的平台.如果是window 则用'nt'表示,对于Linux/Unix用户,它是'posix' import os print(os.name) #结果如下 ...
- Jmeter 结构体系及运行顺序
一.Jmeter 运行原理: Jmeter 时以线程的方式来运行的(由于Jmeter 是 java 开发的所以是运行在 JVM 虚拟机上的,java 也是支持多线程的) 二.Jmeter 结构体系 1 ...
- FastDFS文件服务器安装指南附安装包和自启动(看此篇就够了)
安装包在最后,本文为博主自己亲自安装记录 转载请注明出处 注意文字不清晰请放大看,放大看!! 安装包地址
- 多阶段构建Golang程序Docker镜像
Docker简介 Docker是基于Linux容器技术(LXC),使用Go语言实现的开源项目,诞生于2013年,遵循Apache2.0协议.Docker自开源后,受到广泛的关注和讨论. Docker在 ...
- Qt for Android (三) 打开Android相册并选一个图片进行显示
Qt for Android (三) 这两天弄了一下android相册的相关功能.还是花了挺长时间的,这里总结一下,避免以后再踩坑.同时也在这篇文章里面补齐一些android开发的基础支持 打开And ...
- 阿里巴巴泰山版《Java 开发者手册》,也是一份防坑指南
我是风筝,公众号「古时的风筝」,一个不只有技术的技术公众号,一个在程序圈混迹多年,主业 Java,另外 Python.React 也玩儿的 6 的斜杠开发者. Spring Cloud 系列文章已经完 ...
- Linux 物理卷(PV)、逻辑卷(LV)、卷组(VG)管理
(一)相关概念 逻辑卷是使用逻辑卷组管理(Logic Volume Manager)创建出来的设备,如果要了解逻辑卷,那么首先需要了解逻辑卷管理中的一些概念. 物理卷(Physical Volume, ...