注:相关信息已进行了脱敏处理,确保敏感信息不被泄露
代码简洁思考
纬度:可读性、可维护性、美观性
一、结构化数据管理
通过使用结构体来集中管理相关数据,避免散乱的全局变量和重复的参数传递。对于相关的数据,考虑将它们封装成结构体或对象,这样不仅能更好地组织代码,还能清晰表达数据的用途
实践:在某些任务执行队列逻辑中,有多处逻辑需要将任务从执行队列投递到重试队列进行延迟执行,所以将重试队列的数据封装为 RetryTaskData
结构体,参数可以包含重试标志、延迟时间等信息
type RetryTaskData struct {
isRetry bool
delayTime int
}
二、单一职责原则、简化逻辑表达
确保每个函数或方法仅承担单一的责任,避免功能过于复杂,遵循单一责任原则,将复杂的逻辑分解成多个简单的函数。简化逻辑表达,避免冗长、重复的逻辑表达式,以及嵌套过深的条件判断与循环
实践一:在任务执行队列中,将重试的逻辑提取到一个单独的函数中,使得每个函数仅做一件事
// 任务重新投递
func RetryTask(ctx context.Context, taskId int64, delayTime int64, dayTime int64) (err error) {
tag := "TaskRetry"
// 构造结构体,序列化
retryTask := Task{
TaskId: taskId,
}
retryTaskJson, err := json.Marshal(retryTask)
if err != nil {
logger.Ex(ctx, tag, "数据序列化失败, taskId:%s, err: %v", taskId, err)
return
}
delayTime = time.Now().Unix() + delayTime
if dayTime > 0 {
delayTime = dayTime
}
// 投递到指定队列
err = delay.DelaySendMessageNx(ctx, rediskey.NewTaskQueue, retryTaskJson, delayTime)
if err != nil {
logger.Ex(ctx, tag, "数据写入失败, taskId:%s, err: %v", taskId, err)
return
}
logger.Ix(ctx, tag, "数据写入成功, taskId:%s", taskId)
return
}
实践二:在某些统计脚本中,将按主体ID走的逻辑抽离出来放到 run
方法中,提高代码的美观性与可读性
func (r *Stats) Run(ctx context.Context) {
r.ctx = ctx
tag := `cron.Stats.Run`
logger.Ix(r.ctx, tag, "start cron work")
// 获取有效的主体ID
corpList, err := corp.GetCorpList(ctx, GetCorpListReq{})
if err != nil {
logger.Ex(ctx, tag, "查询主体列表失败, err:%+v", err)
return
}
// 执行统计脚本
for _, corp := range corpList {
SyncStats(ctx, corp.Id)
}
logger.Ix(r.ctx, tag, "cron work done")
}
三、defer简化资源释放与重试逻辑
使用 defer
来延迟执行一些清理工作或后续操作,尤其适合处理如重试机制等逻辑,避免在逻辑中插入冗余的代码
实践一:使用 defer
来统一执行重试操作,避免重复的代码
defer func() {
if retryData.isRetry {
log.Infof("Retrying with delay %d", retryData.delayTime)
time.Sleep(time.Duration(retryData.delayTime) * time.Second)
RetryTask() // 重试任务
}
}()
实践二:使用 defer
来执行导出操作,如文件导出等
// 创建文件
file := NewFile()
sheet := "Sheet1"
// 设置单元格样式
style := Style{
Alignment: &Alignment{
Horizontal: "left",
Vertical: "center",
WrapText: true,
},
}
styleID, err := file.NewStyle(&style)
if err != nil {
logger.Ex(ctx, tag, fmt.Sprintf("创建样式失败 err = %+v", err))
return
}
// 设置列样式
_ = file.SetColStyle(sheet, "A", styleID)
// 设置表头
headers := []string{
"统计日期", "有效链接数量", "日访问uv", "日新增客户", "累计访问uv", "累计新增客户", "结余使用量",
}
for col, header := range headers {
cell := fmt.Sprintf("%s1", string(rune('A'+col)))
_ = file.SetCellValue(sheet, cell, header)
}
// 导出文件
defer func() {
if err != nil {
logger.Ex(ctx, tag, "导出因为错误不执行,err: %+v", err)
return
}
fileName := fmt.Sprintf("统计数据表_%s_%s.xlsx", GetDate(), GenerateRandomStr(6))
ctx.Header("Content-Type", "application/octet-stream")
ctx.Header("Content-Disposition", `attachment; filename=`+fileName)
ctx.Header("Content-Transfer-Encoding", "binary")
err = file.Write(ctx.Writer)
if err != nil {
logger.Ex(ctx, tag, `写入文件异常, req: %#v, err: %v`, req, err)
}
return
}()
四、其他
- 避免重复代码与魔法数字:减少重复代码,避免硬编码的数字或常量,提升代码的可读性和可维护性,使用常量和配置代替魔法数字,使得代码更具可配置性
- 合理的日志记录与注释:通过适当的日志记录和注释,帮助后续开发人员快速理解代码的意图和流程,记录重要的操作,特别是错误和警告信息
- 条件判断的优化与清晰化:简化复杂的条件判断,通过提取条件判断的逻辑到独立的函数,增加可读性,比如将环境判断等条件逻辑提取为单独的函数或变量,避免冗长的 if 语句
// 函数使用
if isProductionEnvironment() {
// 处理生产环境逻辑
}
- 统一的命名规范:确保命名统一且有意义,遵循一致的命名规范,避免含糊不清或过于复杂的名称。使用简洁且明确的命名,确保代码在阅读时能够一眼看出其作用
- 避免全局状态:避免过多的全局状态或共享数据,尽量将数据封装在局部作用域中,传递参数而非依赖全局变量,避免不必要的副作用,保证代码的模块化和独立性