Golang 中的良好代码与糟糕代码

2023年 11月 23日 37.2k 0

最近,有人要求我详细解释在 Golang 中什么是好的代码和坏的代码。我觉得这个练习非常有趣。实际上,足够有趣以至于我写了一篇关于这个话题的文章。为了说明我的回答,我选择了我在空中交通管理(ATM)领域遇到的一个具体用例。

一、背景

首先,简要解释一下实现的背景。

欧洲航空管制组织(Eurocontrol)是管理欧洲各国航空交通的组织。Eurocontrol 与航空导航服务提供商(ANSP)之间交换数据的通用网络称为 AFTN。这个网络主要用于交换两种不同类型的消息:ADEXP 和 ICAO 消息。每种消息类型都有自己的语法,但在语义上,这两种类型是等价的(或多或少)。在这个上下文中,性能 必须是实现的关键要素。

该项目需要提供两种基于 Go 解析 ADEXP 消息的实现(ICAO 没有在这个练习中处理):

  • 一个糟糕的实现(包名:bad)
  • 一个重构后的实现(包名:good)

可以在 这里 找到 ADEXP 消息的示例。

在这个练习中,解析器只处理了 ADEXP 消息中的一部分字段。但这仍然是相关的,因为它可以说明常见的 Golang 错误。

二、解析

简而言之,ADEXP 消息是一组令牌。令牌类型可以是:一组令牌的重复列表。每行包含一组令牌子列表(在本示例中为 GEOID、LATTD、LONGTD)。

考虑到这个背景,重要的是要实现一个可以利用并行性的版本。所以算法如下:

  • 预处理步骤来清理和重新排列输入消息(我们必须清除潜在的空格,重新排列多行的令牌,如 COMMENT 等)。
  • 然后在一个给定的 goroutine 中拆分每一行。每个 goroutine 将负责处理一行并返回结果。
  • 最后,收集结果并返回一个 Message 结构。这个结构是一个通用的结构,无论消息类型是 ADEXP 还是 ICAO。

每个包都包含一个 adexp.go 文件,暴露了主要的函数 ParseAdexpMessage()。

三、逐步比较

现在,让我们逐步看看我认为是糟糕代码的部分,以及我是如何重构它的。

1.字符串 vs []byte

糟糕的实现仅处理字符串输入。由于 Go 提供了对字节操作的强大支持(基本操作如修剪、正则表达式等),并且考虑到输入很可能是 []byte(考虑到 AFTN 消息是通过 TCP 接收的),实际上没有理由强制使用字符串输入。

2.错误处理

糟糕的实现中的错误处理有些糟糕。 我们可以找到一些潜在错误返回的情况,而第二个参数中的错误甚至没有被处理:

preprocessed, _ := preprocess(string)

优秀的实现处理了每一个可能的错误:

preprocessed, err := preprocess(bytes)
if err != nil {
  return Message{}, err
}

我们还可以在糟糕的实现中找到一些错误,就像下面的代码中所示:

if len(in) == 0 {
  return "", fmt.Errorf("Input is empty")
}

第一个错误是语法错误。根据 Go 的规范,错误字符串既不应该大写,也不应该以标点结束。

第二个错误是因为如果一个错误字符串是一个简单的常量(不需要格式化),使用 errors.New() 更为高效。

优秀的实现看起来是这样的:

if len(in) == 0 {
    return nil, errors.New("input is empty")
}

3.避免嵌套

mapLine() 函数是一个避免嵌套调用的良好示例。糟糕的实现:

func mapLine(msg *Message, in string, ch chan string) {
if !startWith(in, stringComment) {
token, value := parseLine(in)
if token != "" {
f, contains := factory[string(token)]
if !contains {
ch

相关文章

JavaScript2024新功能:Object.groupBy、正则表达式v标志
PHP trim 函数对多字节字符的使用和限制
新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
为React 19做准备:WordPress 6.6用户指南
如何删除WordPress中的所有评论

发布评论