问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

避免 else 语句会让你成为更优秀的 Go 程序员

创作时间:
作者:
@小白创作中心

避免 else 语句会让你成为更优秀的 Go 程序员

引用
1
来源
1.
https://learnku.com/go/t/55494

在过去的几年里,我作为一名专业的程序员,经历了快速的成长。从实习生到负责服务超过16万用户、覆盖140多个国家的产品的首席工程师,我积累了丰富的经验。最近,我回顾了这些年用各种语言(包括Haskell、Scala、Go、Python、Java和JavaScript)编写的代码,发现了一个显著的趋势:我几乎不使用else语句。

视线规则

我坚信未来的代码会更注重易读性,而不是执行效率。对此,我十分认同 Donald Knuth 的观点:

「程序最重要的是易读性,而不是执行效率」 - Donald Knuth, 《计算机编程艺术》.

理解代码是一种主观能力,很难明确什么样的代码更通俗易懂。一个公认的规则是视线规则,一个在 Go 社区受欢迎的规则。Mat Ryer 在他的谈话和文章中表达了对该规则的推崇。简单的说,我们应该尽可能缩小代码中的「快乐路径」。Ps:快乐路径代指代码上下文关联的长度。相反,任何错误处理或特殊情况代码都应进一步缩进。

任何遵循这一点的代码都有一个独特的属性:扫描缩进最少的代码就足以理解任何代码段在做什么。扫描缩进更多的代码会显示所有可能发生的特殊情况和错误。这使得它非常容易一眼就能理解。

那么其他语句与此有什么关系呢?

Else 语句是有问题的,因为它们迫使代码向下缩进一级。我们突然不清楚什么代码与「快乐路径」相关,以及什么是特例。

这种不清晰的情况使代码更难扫描,并且影响了可读性。

缺乏上下文

快速有效地扫描代码的能力非常重要。孤立地消化代码的小部分是其中的一个关键部分。我们不希望总是必须阅读每一行代码才能理解代码库的一小部分。

Else 语句将 if 条件和受其影响的代码隔开,从而使这一点变得更加困难。这最好通过两个例子来解释。首先,你能告诉我当这三行代码运行时会发生什么吗?

if myVariable == nil { 
    return “”
}

希望这种写法是相当明显的。让我们举一个相反的例子:

} else { 
    return “”
}

我们可以看到没有 if 语句,我们无法确定这是什么意思。为什么它会返回一个空字符串?这是一个错误,还是「正常」行为?相反,这段代码依赖于我们记住并阅读了前面的上下文。当语句很小时,这并不重要,但是如果在 if{…} 块中有复杂的逻辑,或者我们正在快速扫描,那么上下文与代码的分离会极大地损害可读性。如果 if/else 语句是嵌套的,或者一个函数中有多个 if/else 语句( 这是哪个 if 语句?)。

如何去掉 else 语句?

现在我们一致认为 else 语句是垃圾。但这并没有什么用。真正的诀窍是如何避开他们。值得庆幸的是,有如下两种简单的方法:

  • 反转 if 条件,尽早返回,
  • 创建辅助函数。

反转条件

这是我遇到的最常见的例子。它也可以有两种形式,一种 else 是隐式的,另一种是显式的。显式版本如下所示:

func doSomething() error {
  if something.OK() {
    err := something.Do()
    if err != nil {
      return err
    }
  } else {
    return nil, errors.New("something isn't ok")
  }
}

隐式类似,但不包含 else 语句本身。相反, else 是通过简单地删除函数的结尾来表示的(这个在 Python 或 JavaScript 中更常见,其中 None 或 undefined 如果没有明确说明,则返回)。

function doSomething() {
  if (something.OK()) {
    return something.Do()
  }
}

同样,这并不能让我们了解代码的全部行为。如果没有读取整个函数,则返回值尚不清楚。

通过简单地反转 if 条件,我们可以解决所有这些问题。

function doSomething() {
  if (!something.OK()) {
    // return or throw error
  }
  return something.Do()
}

我们现在可以扫描此函数,清楚地看到缩进错误条件和正常流程,满足视线规则。行为是完全明确的,我们没有上下文分离。这更好。

辅助函数

我们还得到了不会直接导致 return 的 else 语句。这通常是通过一些未正确隔离的特殊情况逻辑实现的。例如

let charities
if (country != "") {
  if (tier != "") {
    charities = getCharitiesByCampaignCountryAndTier(campaign, country, tier)
  } else {
    charities = getCharitiesByCampaignAndCountry(campaign, country)
  }
} else {
  charities = getCharitiesByCampaign(campaign)
}
// do something with charities

通过将 charity 获取逻辑写到自己的方法中,可以改善这块的可读性。这将让特殊情况适当处理,并提前返回。通过反转一些 if 语句,可以进一步提高这一点。

例如:

function getCharities(campaign, country, tier) {
  if (country == "") {
    return getCharitiesByCampaign(campaign)
  }
  if (tier == "") {
    return getCharitiesByCampaignAndCountry(campaign, country)
  }
  return getCharitiesByCampaignCountryAndTier(campaign, country, tier)
}

这个辅助函数巧妙的封装了我们需要的所有逻辑,删除了对任何其他语句的需要,并且在保持快乐路径方面做的更好。这更容易浏览,因此可读性更高。

结论

Else 语句的代码味道很奇怪。它们通过强制相等级别的缩进来处理错误和获得满意的路径,从而损害了任何代码的可读性。它们还具有将代码与影响代码的逻辑分离的独特能力。通过提前返回和将逻辑拆分为辅助函数这两种技术,它们很容易避免。因此,它们是不必要的。你可以通过不使用它们来编写更好的代码,成为更好的程序员。

一些注意事项 (以阻止学究)。

  • 在 SQL 中,当 ...ELSE... 不是真的可以避免时。
  • 在 Scala 中,隐式返回(避免使用 return 语句以实现引用透明性)意味着您必须使用它们-您真的没有「提前返回」的能力。
  • 三元运算符很好。
  • 在 Python 中, 三元运算符使用 else 。这也很好。
© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号