设为首页 加入收藏

TOP

Go 语言实践(一)(四)
2018-11-29 18:11:19 】 浏览:497
Tags:语言 实践
res lots of comments. — Dave Thomas and Andrew Hunt, The Pragmatic Programmer 好的代码中附带有大量的注释,坏的代码缺少大量的注释。

代码注释对 Go 程序的可读性极为重要。一个注释应该做到如下三个方面的至少一个:

  1. 注释应该解释“做什么”。
  2. 注释应该解释“怎么做的”。
  3. 注释应该解释“为什么这么做”。

第一种形式适合公开的符号:

// Open opens the named file for reading.
// If successful, methods on the returned file can be used for reading.

第二种形式适合方法内的注释:

// queue all dependant actions
var results []chan error
for _, dep := range a.Deps {
        results = append(results, execute(seen, dep))
}

第三种形式,“为什么这么做”,这是独一无二的,无法被前两种取代,也无法取代前两种。第三种形式的注释用于解释更多的状况,而这些状况往往难以脱离上下文,否则将没有意义,这些注释就是用来阐述上下文的。

return &v2.Cluster_CommonLbConfig{
  // Disable HealthyPanicThreshold
  HealthyPanicThreshold: &envoy_type.Percent{
    Value: 0,
  },
}

在这个示例中,很难立即弄清楚把HealthyPanicThreshold的百分比设置为零会产生什么影响。注释就用来明确将值设置为0实际上是禁用了panic阈值的这种行为。

变量和常量上的注释应当描述它的内容,而非目的

我之前谈过,变量或常量的名称应描述其目的。向变量或常量添加注释时,应该描述变量的内容,而不是定义它的目的

const randomNumber = 6 // determined from an unbiased die

这个示例的注释描述了“为什么”randomNumber被赋值为 6,也说明了 6 这个值是从何而来的。但它没有描述randomNumber会被用到什么地方。下面是更多的例子:

const (
    StatusContinue           = 100 // RFC 7231, 6.2.1
    StatusSwitchingProtocols = 101 // RFC 7231, 6.2.2
    StatusProcessing         = 102 // RFC 2518, 10.1

    StatusOK                 = 200 // RFC 7231, 6.3.1

如在 RFC 7231 的第 6.2.1 节中定义的那样,在 HTTP 语境中 100 被当做StatusContinue

小窍门:对于那些没有初始值的变量,注释应当描述谁将负责初始化它们 // sizeCalculationDisabled indicates whether it is safe // to calculate Types' widths and alignments. See dowidth. var sizeCalculationDisabled bool 这里,通过注释让读者清楚函数dowidth在负责维护sizeCalculationDisabled的状态。 小窍门:隐藏一目了然的东西 Kate Gregory 提到一点^3,有时一个好的命名,可以省略不必要的注释。 // registry of SQL drivers var registry = make(mapstringsql.Driver) 注释是源码作者加的,因为registry没能解释清楚定义它的目的——它是个注册表,但是什么的注册表? 通过重命名变量名为sqlDrivers,现在我们很清楚这个变量的目的是存储 SQL 驱动。 var sqlDrivers = make(mapstringsql.Driver) 现在注释已经多余了,可以移除。

总是为公开符号写文档说明

因为 godoc 将作为您的包的文档,您应该总是为每个公开的符号写好注释说明——包括变量、常量、函数和方法——所有定义在您包内的公开符号。

这里是 Go 风格指南的两条规则:

  • 任何既不明显也不简短的公共功能必须加以注释。
  • 无论长度或复杂程度如何,都必须对库中的任何函数进行注释。
package ioutil

// ReadAll reads from r until an error or EOF and returns the data it read.
// A successful call returns err == nil, not err == EOF. Because ReadAll is
// defined to read from src until EOF, it does not treat an EOF from Read
// as an error to be reported.
func ReadAll(r io.Reader) ([]byte, error)

对这个规则有一个例外:您不需要为实现接口的方法进行文档说明,特别是不要这样:

// Read implements the io.Reader interface
func (r *FileReader) Read(buf []byte) (int, error)

这个注释等于说明都没说,它没有告诉您这个方法做了什么,实际上更糟的是,它让您去找别的地方的文档。在这种情况我建议将注释整个去掉。

这里有一个来自io这个包的示例:

// LimitReader returns a Reader that reads from r
// but stops with EOF after n bytes.
// The underlying implementation is a *LimitedReader.
func LimitReader(r Reader, n int64) Reader { return &LimitedReader{r, n} }

// A LimitedReader reads from R but limits the amount of
// data returned to just N bytes. Each call to Read
// updates N to reflect the new amount remaining.
// Read returns EOF when N <= 0 or when the underlying R returns EOF.
type LimitedReader struct {
  R Reader // underlying reader
  N int64  // max bytes remaining
}

func (l *LimitedReader) Read(p []byte) (n int, err error) {
  if l.N <= 0 {
    return 0, EOF
  }
  if int64(len(p)) > l.N {
    p = p[0:l.N]
  }
  n, err = l.R.Read(p)
  l.N -= int64(n)
  return
}

请注意,LimitedReader的声明紧接在使用它的函数之后,并且LimitedReader.Read又紧接着定义在LimitedReader之后,即便LimitedReader.Read本身没有文档注释,那和很清楚它是io.Reader的一种实现。

小窍门:在您编写函数之前先写描述这个函数的注

首页 上一页 1 2 3 4 5 下一页 尾页 4/5/5
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇Golang 学习权威网站 下一篇细说Go语言切片

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目