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


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

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

    else 操作

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

    func handleRequest(user *User) {
        if user != nil {
            showUserProfilePage(user)
        } else {
            showLoginPage()
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

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

    func handleRequest(user *User) {
        if user == nil {
            return showLoginPage()
        } 
        showUserProfilePage(user)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

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

    break 操作

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

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

    func processData(data []int, target int) {
        for i, value := range data {
            if value == target {
                performActionForTarget(data[i])
                break
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

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

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

    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
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

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

    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
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

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

    continue 操作

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

    func processWords(words []string, substring string) {
        for _, word := range words {
            if !strings.Contains(word, substring) {
                continue
            }
            
            // do some buisness logic
            performAction(word)
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

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

    让我们写得更简洁些:

    func processWords(words []string, substring string) {
        for _, word := range words {
            if strings.Contains(word, substring) {
                performAction(word)
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    关注公众号【爱发白日梦的后端】分享技术干货、读书笔记、开源项目、实战经验、高效开发工具等,您的关注将是我的更新动力!

  • 相关阅读:
    lab1-1 恶意代码分析实战
    Pandas 速查手册
    MongoDB 数据库性能优化技巧
    Class文件结构
    【相机方案(2)】V4L2 支持相机图像直接进入GPU内存吗?DeepStream 确实可以将图像数据高效地放入GPU内存进行处理!
    Java 中HashMap 详解
    ISIS与OSPF区别
    k8s部署-kuboard安装(工具kuboard-spary)
    【HTML5】登录页面制作简易版
    DES & 3DES 简介 以及 C# 和 js 实现【加密知多少系列】
  • 原文地址:https://blog.csdn.net/liufotian/article/details/133898679