Go 重构:尽量避免使用 else、break 和 continue

今天,我想谈谈相当简单的事情。我不会发明什么,但我在生产代码中经常看到这样的事情,所以我不能回避这个话题。

我经常要解开多个复杂的 if else 结构。多余的缩进、过多的逻辑只会加深理解。首先,这篇文章的主要目的是让代码更透明、更易读。不过,在某些情况下还是必须使用这些操作符。

else 操作

例如,我们有简单的用户处理程序:

go 复制代码
func handleRequest(user *User) {
    if user != nil {
        showUserProfilePage(user)
    } else {
        showLoginPage()
    }
}

如果没有提供用户,则需要将收到的请求重定向到登录页面。If else 似乎是个不错的决定。但我们的主要任务是确保业务逻辑单元在任何输入情况下都能正常工作。因此,让我们使用提前返回来实现这一点。

go 复制代码
func handleRequest(user *User) {
    if user == nil {
        return showLoginPage()
    } 
    showUserProfilePage(user)
}

逻辑是一样的,但是下面的做法可读性会更强。

break 操作

对我来说,BreakContinue 语句总是可以分解的信号。

例如,我们有一个简单的搜索任务。找到目标并执行一些业务逻辑,或者什么都不做。

go 复制代码
func processData(data []int, target int) {
    for i, value := range data {
        if value == target {
            performActionForTarget(data[i])
            break
        }
    }
}

你应该始终记住,使用 break 操作符并不能保证整个数组都会被处理。这对性能有好处,因为我们丢弃了不必要的迭代,但对代码支持和可读性不利。因为我们永远不知道程序会在列表的开头还是结尾停止。

在某些情况下,带有子任务的简单功能可能会破坏这段代码。

go 复制代码
func processData(data []int, target int, subtask int) {
    for i, value := range data {
        if value == subtask {
            performActionForSubTarget(data[i])
        }
        if value == target {
            performActionForTarget(data[i])
            break
        }
    }
}

这样我们实际上可以拆出一个 find 的方法:

go 复制代码
func processData(data []int, target int, subTarget int) {
    found := findTarget(data, target)
    if found > notFound {
        performActionForTarget(found)
    }

    found = findTarget(data, subTarget)
    if found > notFound {
        performActionForSubTarget(found)
    }
}

const notFound = -1

func findTarget(data []int, target int) int {
    if len(data) == 0 {
        return notFound
    }

    for _, value := range data {
        if value == target {
            return value
        }
    }

    return notFound
}

同样的逻辑,但是拆分成更细粒度的方法,也有精确的返回语句,可以很容易地通过测试来实现。

continue 操作

该操作符与 break 类似。为了正确阅读代码,您应该牢记它对操作顺序的具体影响。

go 复制代码
func processWords(words []string, substring string) {
    for _, word := range words {
        if !strings.Contains(word, substring) {
            continue
        }
        
        // do some buisness logic
        performAction(word)
    }
}

Continue 使得这种简单的流程变得有点难以理解。

让我们写得更简洁些:

go 复制代码
func processWords(words []string, substring string) {
    for _, word := range words {
        if strings.Contains(word, substring) {
            performAction(word)
        }
    }
}
相关推荐
狼爷14 小时前
百万QPS多场次秒杀系统架构全解:解耦设计、防超卖、流量防护体系
后端·架构
ruxingli15 小时前
Golang iota详解
开发语言·后端·golang
用户3983461612015 小时前
Go-Spring 实战第 15 课 —— Condition:根据配置和上下文激活 Bean
spring·go
前端环境观察室15 小时前
别只看 task success:AI Agent 浏览器自动化真正要补的是环境证据链
前端·后端
浩风祭月15 小时前
把 Docker 镜像从 2GB 瘦身到 180MB,AI 帮我找到了那些看不见的“脂肪”
后端·ai编程
暗冰ཏོ16 小时前
Go 语言从入门到后端项目实战完整指南
开发语言·后端·golang·go·go语言
霸道流氓气质17 小时前
Windows批处理脚本完整指南:可移植的交互式SpringBoot项目管理
windows·spring boot·后端
小杍随笔17 小时前
【Rust 工具链管理完全指南:rustup toolchain 命令实战详解】
开发语言·后端·rust
还是鼠鼠17 小时前
AI掘金头条新闻系统 (Toutiao News)-获取用户信息
后端·python·mysql·fastapi·web
BingoGo17 小时前
免费可商用 PHP 管理后台 CatchAdmin V5.3.1 发布 后台打包直降 5s 内
后端·php