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 ...
随机推荐
- MySQL - Show Global Status 整理
2019独角兽企业重金招聘Python工程师标准>>> MySQL - Show Global Status 整理 原文来源:MySQL 5.5 Reference Manual 部 ...
- vSphere可用性之三准备实验环境
第三章 准备实验环境 在上篇内容中,讲述了进行VMware HA实验所必需的软硬件条件.接下来将使用这些来搭建实验环境.主要内容为依据拓扑图安装ESX主机系统.ISCSI存储系统. 此次实验环境的建置 ...
- 数学--数论--HDU 6063 RXD and math (跟莫比乌斯没有半毛钱关系的打表)
RXD is a good mathematician. One day he wants to calculate: output the answer module 109+7. p1,p2,p3 ...
- 10.秋招复习简单整理之CSMA/CD协议
我们知道,总线上只要有一台计算机在发送数据,总线的传输资源就被占用.因此,在同一时间只能允许一台计算机发送数据,否则各计算机之间就会相互干扰,使得所发送的数据被破坏.因此,如何协调总线上各计算机的工作 ...
- docker批量删除本地镜像和容器
长时间运行docker,每次只用docker kill去停止容器,但是从没删除过本地镜像,导致有上百个镜像在占用内存. 1.批量停止容器 docker container stop $(docker ...
- python操作ansible api示例
#!/usr/bin/env python # -*- coding:utf-8 -*- import json import shutil from collections import named ...
- redis-py中的坑
今天发现,使用redis-py从redis中获取的数据竟然是加密的. conn = redis.Redis(host='redis_serverip', port=6379, password='re ...
- print函数的全面认识
# 输出打印 数字 print(123) a = 100 print(a) # 输出打印 字符串 print('字符串123') print('''锄禾日当午 汗滴禾下土''') # 输出打印 列表 ...
- 什么情况下不能使用 Java 泛型
1. 前言 Java 1.5 引入了泛型来保证类型安全,防止在运行时发生类型转换异常,让类型参数化,提高了代码的可读性和重用率.但是有些情况下泛型也是不允许使用的,今天就总结一下编码中不能使用泛型的一 ...
- 设计模式之GOF23建造者模式
组件很多,装配顺序不定 本质: 1,分离了对象子组件的单独构造(Builder负责)和装配(Director负责),从而可以构造出复杂的对象,这个模式适用于某个对象的构建过程复杂的情况下使用 2,实现 ...