一个看似不重要,却常常让人很纠结的问题

多返回值

Gopher们应该都很清楚golang有个特性,就是支持多返回值,并且支持对多返回值进行命名。但是,命名不是强制要求的,这样就引出了针对这一特性的两种编码风格,如下:

  • 命名风格
go 复制代码
func laughWithNamed(i int) (hahaCount int) {
    for ; i > 0; i-- {
       hahaCount++
    }
    return
}
  • 匿名风格
go 复制代码
func laughWithoutNamed(i int) int {
    hahaCount := 0
    for ; i > 0; i-- {
       hahaCount++
    }
    return hahaCount
}

defer处理

从上面的函数实现来看,命不命名都差不多,那如果对函数增加defer处理:hahaCount > 10时,打印"laugh too much, take care" 并且将hahaCount赋值为0,我们来看看两种风格要怎么改造?

针对命名的风格,将代码改造如下:

go 复制代码
func laughWithNamed(i int) (hahaCount int) {
    defer func() {
       if hahaCount > 10 {
          fmt.Println("laugh too much, take care")
          hahaCount = 0
       }
    }()
    
    for ; i > 0; i-- {
       hahaCount++
    }
    return
}

写个test测试一下,laugh的次数指定为11:

scss 复制代码
func Test_Laugh(t *testing.T) {
    laughWithNamed(11)
}

结果如下:

=== 复制代码
laugh too much, take care
    main_test.go:6: 0
--- PASS: Test_Laugh (0.00s)
PASS

hmm,返回值为0没什么问题。

那我们再试一下对laughWithoutNamed改造如下:

go 复制代码
func laughWithoutNamed(i int) int {
    hahaCount := 0

    defer func() {
       if hahaCount > 10 {
          fmt.Println("laugh too much, take care")
          hahaCount = 0
       }
    }()

    for ; i > 0; i-- {
       hahaCount++
    }
    return hahaCount
}

写个test测试一下,laugh的次数仍旧指定为11:

scss 复制代码
func Test_Laugh(t *testing.T) {
    laughWithoutNamed(11)
}

结果如下:

=== 复制代码
laugh too much, take care
    main_test.go:6: 11
--- PASS: Test_Laugh (0.00s)
PASS

hmm,返回值为11。。。

在这种场景下,想对返回值通过defer进行处理,使用匿名返回值是无法实现的

你可能会说,那我就不用defer呗,在return之前就进行处理,这里就需要说一下使用defer的前提:我们使用defer,往往是希望避免在return处增加处理。例如unlock操作,如果一个函数,分支很多,较为复杂,那unlock就会遍布各return处,很容易出现疏漏。

代码可读性

命名返回值能够提高代码的可读性,在阅读interface实现时,一个命名的返回值,往往比未命名的返回值有更高的可读性。如下:

go 复制代码
type laughInterface interface {
    laughWithNamed(i int) (hahaCount int)
    laughWithoutNamed(i int) int
}

命名风格很明显比匿名风格更有可读性。

代码简洁

如果要为函数增加返回值,withoutNamed的实现,需要在每个return处都需要增加返回参数,分支较多的情况下,增加返回参数将成为一个繁重的工作。

那么是不是说withoutNamed就一定不简洁呢?也不能这样下结论,当函数较小时,使用withoutNamed的风格定义,代码将看起来反而更简单明了。例如:

go 复制代码
func laughWithoutNamed(name string) string {
    return name + "say haha"
}

这时候如果对返回值进行命名,将会显得很累赘:

go 复制代码
func laughWithoutNamed(name string) (action string) {
    action = name + "say haha"
    return action
}

总结

golang支持的函数多返回值,可以根据实际情况选择命名或者匿名。

个人建议:

  • 当函数逻辑较复杂时,使用命名返回值(Named Results)的风格,可以省去增删返回值时的修改量,并且能够很好地支持defer处理。
  • 当函数逻辑很简单时,使用匿名返回值(Anonymous Results)的风格,代码更加简洁易读。
相关推荐
80530单词突击赢12 分钟前
JavaWeb进阶:SpringBoot核心与Bean管理
java·spring boot·后端
爬山算法32 分钟前
Hibernate(87)如何在安全测试中使用Hibernate?
java·后端·hibernate
WeiXiao_Hyy1 小时前
成为 Top 1% 的工程师
java·开发语言·javascript·经验分享·后端
苏渡苇1 小时前
优雅应对异常,从“try-catch堆砌”到“设计驱动”
java·后端·设计模式·学习方法·责任链模式
long3161 小时前
Aho-Corasick 模式搜索算法
java·数据结构·spring boot·后端·算法·排序算法
rannn_1112 小时前
【苍穹外卖|Day4】套餐页面开发(新增套餐、分页查询、删除套餐、修改套餐、起售停售)
java·spring boot·后端·学习
短剑重铸之日2 小时前
《设计模式》第十一篇:总结
java·后端·设计模式·总结
Dragon Wu3 小时前
Spring Security Oauth2.1 授权码模式实现前后端分离的方案
java·spring boot·后端·spring cloud·springboot·springcloud
一个有梦有戏的人3 小时前
Python3基础:进阶基础,筑牢编程底层能力
后端·python
爬山算法3 小时前
Hibernate(88)如何在负载测试中使用Hibernate?
java·后端·hibernate