5.2 程序文档

程序文档是让程序使用者了解程序行为和特性的最有效的途径之一(另一个途径是“代码即文档”)。

在Go语言中,我们可以很方便地使用godoc命令在本机启动一个可被用于查看本机所有工作区中的所有代码包文档的Web服务。启动命令如下:

  1. godoc -http=:9090 -index

关于godoc命令及其用法的说明,请大家参看我放到Github上的Go命令教程。

既然生成文档和显示文档的工作Go语言已经为我们做好了,那么我们需要做的就是写好程序的注释,为程序文档提供优秀的素材。

1. 编写程序注释

Go语言在注释风格中融入了C语言和C++语言的基因。我们既可以使用C++语言风格的行注释:

  1. // 行注释

也可以使用C语言风格的块注释:

  1. /*
  2. 块注释
  3. */

行注释我们应该已经很熟悉了。它已经在我们的示例中出现过很多次了。行注释能且只能占用单独一行来存放注释的内容。它是最基本的注释方式,并可以出现在源码文件中的任何地方。

块注释是更加灵活的一种注释方式。它可以占据任意行存放注释内容。块注释更适合作为一个代码包的文档性注释。

需要注意的是,无论是哪一种风格的注释都不能出现嵌套的情况。

2. 代码包的注释

每个代码包都应包含一段独立的代码包注释。这段注释应该对当前代码包的功能和用途进行总体性的介绍。按照惯例,代码包注释应该被存放到当前代码包目录下的doc.go文件中。在这个文件中,应该有与包中其他源码文件相同的代码包声明语句,并在该声明语句之上以块注释的方式插入代码包注释。当然,在代码包中只有一个源码文件或者代码包注释比较短的情况下,把代码包注释插入到与当前代码包同名的源码文件中的代码包声明语句之上也未尝不可。顺便说一句,当代码包中仅有一个源码文件的时候,建议源码文件的主文件名与当前代码包的名称相同。

代码包注释会总是会出现在godoc命令生成的对应文档页面的首要位置上。也就是说,代码包注释会作为该代码包的文档的第一段说明出现。所以,我们不但应该花时间编写它,还应该让读过它的人能够对当前代码包有足够正确和清晰的认识。在必要时,我们应该向代码包注释中加入一些列表、注释和相关文档的指引。例如,在标准库中,代码包fmt的代码包注释被放在了$GOROOT/src/fmt/doc.go中。与其相对应的文档页面如图5-4所示。

{%}

图 5-4 代码包fmt的文档页面1

在这个文档页面中,在“Overview”一栏中的就是fmt包的代码包注释。

3. 程序实体的注释

看到上图中的那个紧挨在“Overview”栏上面的蓝色的(意味着带有链接)的目录了吗?其中的“Index”栏包含的是当前代码包中的所有可导出的程序实体的文档的索引。在我们点击其中一个索引(如“type State”)之后,页面会定位到与该索引相对应的程序实体文档的锚点处,如图5-5所示。

{%}

图 5-5 代码包fmt的文档页面2

现在,我们把鼠标放在页面中“type State”的“State”处并点击。页面会跳转到声明fmt.State的源代码处(见图5-6高亮的部分)。

{%}

图 5-6 代码包fmt的文档页面3

纵观上面展示的一系列配图,我们可以了解到,程序实体的文档即是它的声明代码以及紧挨在上面的行注释。这也部分体现了“代码即注释”的思想。如果我们仔细考量程序实体的各部分命名和声明的手法(比如与fmt.State接口类型声明中的每个方法声明对应的那些注释),那么不论是对于它的使用者还是对于它的文档的阅读者来说都会有很大的帮助。

重申一下,程序实体文档中的文字性说明由紧挨在它的声明之上的那几个行注释呈现。

4. 常量和变量的注释

由于Go语言语法允许一次声明一组常量或变量,所以为这样的程序实体声明编写注释会稍有不同。请看如下示例:

  1. // IP address lengths (bytes).
  2. const (
  3. IPv4len = 4
  4. IPv6len = 16
  5. )

这段代码摘自代码包net中的源码文件ip.go。对于这样一组声明来说,我们应该把对它们的描述统一放在关键字const(或关键字var)之上且紧邻的注释行中,而不是分别为每一个常量(或变量)声明添加注释。

Go语言的注释就是普通的文本。因此,我们不要试图使用一些非文本的字符为注释添加多余的格式,比如用HTML标签<br>表示换行、用很多星号组成一个横幅,等等。即使添加了这些字符,godoc也会以普通文本的方式把它们呈现出来。那么,怎样控制注释中的格式呢?实际上,godoc命令会根据上下文来决定是否对注释进行格式化。因此,我们要做的就是让注释在普通文本状态下就显示良好。比如,统一使用制表符来表示缩进、使用空行来分隔标题与主体内容以及各个段落,以及手动的为一些长文本进行换行(godoc命令会忽略它们,但阅读起来就会舒服很多),等等。另外,godoc命令并不会用等宽字体来展现文档。但是也有一个例外。我们可以使用更多的相同数量的制表符(与上下文相比)来表示一个程序片段或表格,像这样:

  1. /*
  2. 下面是一段代码:
  3. var buff bytes.Buffer
  4. for _, e := range s {
  5. buff.WriteString(fmt.Sprint("%v", content))
  6. }
  7. var content1 = buff.String()
  8. 这段代码表明了...
  9. */

这样表示的程序片段和表格中的内容都会以等宽字体呈现,并且换行符和回车符也不会被忽略。

无论注释出现在什么地方以及以怎样的方式表示,注释的内容都应该是符合英语或中文语法规则的句子。作为惯例,注释中的第一句应该是以被注释对象的名称为开头概括性语句。这样能够在我们使用godoc命令进行文档搜索的时候提供更多的便利。

5. 文档中的示例

在代码包的文档页面中还可以包含有针对性的示例代码。实际上,这些示例代码是godoc命令程序自动从代码包中的测试源码文件中取得的。具体的获取目标是,符合命名规则的样本测试函数中的代码。因为这样的示例代码可以与文档页面中的某个程序实体的文档对照起来(紧跟在该程序实体的文档后面出现)。例如,接口类型net.Listener的示例代码被放在net代码包下的样本测试函数ExampleListener中。而在net包的文档页面中,它们的展现效果如图5-7所示。

{%}

图 5-7 代码包net的文档页面

这也正是我们在上一节强调样本测试函数的命名规则的原因。合格的样本测试函数既可以用于程序测试,又有利于文档中的程序示例的合理展示。

至此,我们已经介绍了与Go语言程序文档的展示和编写有关的所有内容。希望通过阅读这些内容,读者能够更积极地为自己的程序提供文档,并使其内容更加规范和翔实。