系列 2 (21~40)
21 注释来说明代码背后的"原因"
提供注释来解释代码背后的原因和目的。
注释不仅应描述代码的作用,还应描述代码执行的原因。这有助于其他开发人员了解您决策的背景和理由。
Good
// Calculate the discount based on customer loyalty.
// We offer a higher discount to customers who have
// been with us for more than 5 years.
func calculateDiscount(years int) float64 {
if years > 5 {
return 0.20 // 20% discount for loyal customers
}
return 0.10 // 10% discount for others
}
Bad
func calculateDiscount(years int) float64 {
if years > 5 {
return 0.20
}
return 0.10
}
在好的例子中,注释解释了折扣逻辑背后的原因,清楚地说明了为什么使用某些值。在坏的例子中,由于缺少注释,代码的目的不明确,使其他人更难理解业务逻辑。
好的注释可以提供从代码中可能无法立即看出的上下文,从而节省代码审查和维护期间的时间。
22 注释以澄清复杂或不明显的逻辑
使用注释来解释代码中复杂或不明显的部分,以便其他人更容易理解。
当代码中的逻辑比较复杂或不太清晰时,注释可以帮助其他人理解代码的作用以及为什么需要这样做。
Good
// Check if the number is a prime number.
// A prime number is only divisible by 1 and itself.
func isPrime(n int) bool {
if n <= 1 {
return false
}
for i := 2; i * i <= n; i++ {
// If n is divisible by any number other than 1 and itself,
// it is not prime.
if n % i == 0 {
return false
}
}
return true
}
Bad
func isPrime(n int) bool {
if n <= 1 {
return false
}
for i := 2; i*i <= n; i++ {
if n%i == 0 {
return false
}
}
return true
}
在好的例子中,注释阐明了检查素数背后的逻辑,使人们更容易理解代码各部分的用途。在坏的例子中,由于缺乏注释,逻辑更难理解,尤其是对于那些不熟悉素数算法的人来说。在我们写代码的时候时刻记得我们的代码不仅是给自己看的,也是给别人看的。
清晰的注释可以大大降低团队新成员的学习曲线,并通过使复杂的逻辑更易于理解来提高代码的整体质量。
23 用于记录公共API和导出实体的注释
使用注释清楚地记录公共 API 和导出实体的目的和用途。
在编写 Go 代码时,必须记录公共 API 和导出实体,以确保其他开发人员了解它们的用途以及如何正确使用。
Good
// Add adds two integers and returns the result.
// It takes two parameters, a and b, which are the integers to be added.
func Add(a, b int) int {
return a + b
}
// User represents a user in the system.
// It contains the user's ID, name, and email.
type User struct {
ID int // ID is the unique identifier for the user.
Name string // Name is the user's full name.
Email string // Email is the user's email address.
}
Bad
func Add(a, b int) int {
return a + b
}
type User struct {
ID string
Name string
Email string
}
在好的例子中,注释用于解释 Add 函数和 User 结构体的用途,以及它们的参数和字段。这使得代码更容易理解和使用。在坏的例子中,缺少注释使得函数和结构体的用途不清楚,这会导致混淆和误用。如果是你自己的开源项目,最好给公共 API 和导出实体详细的注释,以帮助使用者和学习者更快地理解,反过来这也会有助于你项目的传播。
在 Go 中,导出实体的注释应以实体名称开头。这是一种惯例,可帮助 godoc 等工具自动生成文档。
24 提供上下文或背景信息的注释
使用注释提供上下文或背景信息,帮助其他人理解代码。
在注释中提供上下文或背景信息,可以帮助其他开发人员理解为什么要做出某些决定,或者一段代码是如何融入更大的系统中的。
Good
// CalculateDiscount calculates the discount for a given price.
// The discount rate is based on the current promotion, which is fetched from the database.
func CalculateDiscount(price float64) float64 {
// Fetch the current promotion from the database.
// This is a temporary solution until new new promotion,
// which is fetched from the database.
promotion := fetchCurrentPromotion()
return price * (1 - promotion)
}
// fetchCurrentPromotion fetches the current promotion from the database.
// This function will be replaced by a call to the new promotion service in the future.
func fetchCurrentPromotion() Promotion {
// Simulated database fetch
return Promotion{DiscountRate: 0.1}
}
Bad
func CalculateDiscount(price float64) float64 {
promotion := fetchCurrentPromotion()
return price * (1 - promotion.DiscountRate)
}
func fetchCurrentPromotion() Promotion {
return Promotion{DiscountRate: 0.1}
}
在好的例子中,注释提供了有关计算折扣函数和获取当前促销函数的背景信息,解释了当前的实现和未来计划。这有助于其他开发人员了解代码背后的原因以及未来的预期。在坏的例子中,缺乏注释使其他开发人员无法获得任何背景信息,这使得他们更难理解代码的用途和未来的变化。
在多个开发人员在同一代码库上工作的协作项目中,在注释中提供上下文尤为重要。它有助于保持代码质量,确保每个人都站在同一起跑线上。
25 注释以解释任何假设或依赖关系
清楚地说明代码中的任何假设或依赖关系,以帮助其他人理解上下文。
编写 Go 代码时,记录代码所依赖的任何假设或依赖关系非常重要。这有助于其他开发人员了解实现背后的背景和原理。
Good
// Assumes that the input slice is sorted in ascending order
// Dependency: Uses the "sort" package for binary search.
func findElement(slice []int, target int) int {
index := sort.Search(len(slice), func(i int) bool {
return slice[i] >= target
})
if index < len(slice) && slice[index] == target {
return index
}
return -1
}
Bad
func findElement(slice []int, target int) int {
index := sort.Search(len(slice), func(i int) bool {
return slice[i] >= target
})
if index < len(slice) && slice[index] == target {
return index
}
return -1
}
在好的示例中,注释清楚地说明了输入片段已排序的假设,并提到了对 "排序" 包的依赖。这使得其他人更容易理解函数的上下文和要求。而糟糕的示例缺少这些注释,使人更难理解代码的前提条件。
在 Go 中,sort 包提供了用于对切片进行排序和执行二分搜索的实用函数,这对于排序数据非常有效。
26 注释简明扼要,切中要害
撰写的注释要简短,并与所描述的代码直接相关。
注释应简短、重点突出,提供足够的信息来阐明代码,而不至于让读者不知所措。避免冗长或多余的注释。
简洁与冗余可能只有几字之差,好注释和坏注释同样如此。
Good
// Calculate the factorial of a number using recursion
func factorial(n int) int {
if n <= 1 {
return 1
}
return n * factorial(n-1)
}
Bad
// This function calculates the factorial of a number. The factorial of a number n is the product of all positive integers less than or equal to n. For example, the factorial of 5 is 5*4*3*2*1 = 120. This function uses recursion, which means it calls itself with a decremented value of n until it reaches the base case where n is less than or equal to 1, at which point it returns 1.
func factorial(n int) int {
if n <= 1 {
return 1
}
return n * factorial(n-1)
}
好的示例提供了简洁的注释,解释了函数的用途,没有不必要的细节。另一方面,坏的示例则包含冗长的解释,可能会让人不知所措,分散了人们对代码本身的注意力。保持注释简短且相关有助于保持可读性和重点。
能用一句话说清楚的事情就不要使用两句话,因为总有一句是废话。
在Go中,注释是代码文档的重要组成部分,可以使用godoc等工具来生成文档。
27 确保注释准确反映代码
撰写注释,准确描述代码的作用。
注释应该对代码的功能提供清晰、准确的解释,以帮助其他人快速理解。
Good
// CalculateSum adds two integers and returns the result
func CalculateSum(a, b int) int {
return a + b
}
Bad
// This function does something with numbers.
func CalculateSum(a, b int) int {
return a + b
}
好的注释可以帮助别人更好地阅读代码,坏的注释不仅起不到这个基本作用,还可能会误导读者,贻害无穷。
在好的例子中,注释清楚地说明了该函数将两个整数相加并返回结果,使其他人很容易理解该函数的用途。在坏的例子中,注释含糊不清,没有提供有关该函数用途的有用信息。
准确的注释可以显著减少代码审查和调试所需的时间,因为它们提供了直接的背景和对代码目的的理解。
28 当代码更改时更新注释
始终修改注释以匹配对代码所做的任何更改。
修改代码时,请确保更新注释以反映新的功能或逻辑,以避免混淆。
Good
// CalculateSum adds two integers and returns the result.
// If the sum exceeds 100, it returns 100.
func CalculateSum(a int, b int) int {
sum := a + b
if sum > 100 {
return 100
}
return sum
}
Bad
// CalculateSum adds two integers and returns the result.
func CalculateSum(a int, b int) int {
sum := a + b
if sum > 100 {
return 100
}
return sum
}
注释也是整个代码库的一部分,也需要对其进行更新和维护。
在好的示例中,注释已更新,以反映新的逻辑,即总和上限为 100。而在坏的示例中,注释已经过时,没有提及新的行为,这可能会误导阅读代码的人。
保持注释为最新对于维护代码质量和确保文档准确且对未来的开发人员有帮助至关重要。
29 在注释中使用清晰一致的语言
注释应以清晰一致的语言撰写,并使用正确的语法和拼写。
在 Go 代码中编写注释时,重要的是确保其他可能在代码库上工作的开发人员能够轻松理解它们。
Good
// calculateArea calculates the area of a rectangle
// given its length and width.
func calculateArea(length, width float64) float64 {
return length * width
}
Bad
// calc area of rect
// l & w r params
func calculateArea(length, width float64) float64 {
return length * width
}
在好的代码示例中,注释清楚地解释了函数的用途和参数的含义。语言一致,语法和拼写正确。相反,坏的代码示例使用缩写和不完整的句子,使得代码的意图更难理解。
一致且清晰的注释可以显著提高代码的可读性和可维护性,尤其是在较大的代码库中或团队工作时。
30 避免多余或不必要的注释
注释不应陈述显而易见的内容,也不应重复代码本身已经明确的信息。
虽然注释对于解释代码的意图和目的很重要,但它们不应该简单地以文字方式重述代码的作用。
Good
// calculateArea calculates the area of a rectangle
func calculateArea(length, width float64) float64 {
return length * width
}
Bad
// Multiply length and width
func calculateArea(length, width float64) float64 {
// Multiply length and width
return length * width
}
不要使用注释解释代码的 how,在有必要的情况下,应该解释代码的 why。
在好的代码示例中,注释提供了有关函数用途的有用信息,而无需重复显而易见的内容。在坏的代码示例中,注释只是重复代码的作用,这是多余且不必要的。
冗余或不必要的注释会使代码库变得混乱,难以维护,因为如果代码发生变化,它们可能会变得过时或产生误导。
31 遵循 Go 的惯用编码风格和约定
遵守 Go 的惯用编码风格和约定可确保您的代码一致、可读且可维护。
遵循 Go 的惯用编码风格涉及使用 Go 社区广泛接受的既定模式和实践。
Good
package main
import (
"fmt"
)
// Person struct follows Go naming conventions
type Person struct {
Name string
Age int
}
// NewPerson is a constructor function with a clear name
func NewPerson(name string, age int) *Person {
return &Person{Name: name, Age: age}
}
func main() {
p := NewPerson("Alice", 30)
fmt.Println(p)
}
Bad
package main
import (
"fmt"
)
// person struct not follows Go naming conventions
type person struct {
name string
age int
}
// new_person is not a clear function name.
func new_person(n string, a int) *person {
return &person{Name: n, Age: a}
}
func main() {
p := new_person("Alice", 30)
fmt.Println(p)
}
在好的例子中,Person 结构体和 NewPerson 函数遵循 Go 的命名约定,使代码更易读、更易于维护。坏的例子使用了小写名称和下划线,这在 Go 中并不符合习惯。
Go的习惯风格通常被称为 "Go习语(Go idioms)",并且在 "Effective Go" 和 "Go Code Review Comments" 等资源中有文档记录。
32 使用 Go 的内置格式化工具,如 gofmt 和 goimports
使用 Go 的内置格式化工具可确保您的代码自动遵循一致的风格。
Go 提供 gofmt 和 goimports 等工具来自动格式化你的代码并管理导入,从而更容易维护一致的代码库。
Good
package main
import (
"fmt"
)
func main() {
fmt.Println("Hello, World!")
}
Bad
package main
import (
"fmt"
)
func main() {
fmt.Println("Hello, World!")
}
几乎所有支持 Go IDE 都内置了 gofmt, 编辑完代码保存时会自动进行代码格式化。
好的示例使用 gofmt 进行格式化,确保缩进和间距正确。坏的示例缺乏正确的格式,使其更难阅读。使用 goimports 还可以通过自动管理导入语句来提供帮助。
gofmt 工具对于 Go 来说是如此重要,以至于 Go 团队认为无法通过 gofmt 的代码是错误的。
33 采用一致的缩进和空格
使用一致的缩进和空格来增强代码的可读性和可维护性。
一致的缩进和空格可让其他人更轻松地阅读和理解您的代码。它有助于直观地构建代码,使代码块的开始和结束位置清晰可见。
Good
package main
import "fmt"
func main() {
// Consistent indentation with 4 spaces
for i := 0; i < 10; i++ {
fmt.Println(i) // Print numbers from 0 to 9
}
}
Bad
package main
import "fmt"
func main() {
for i := 0; i < 10; i++ {
fmt.Println(i) // Print numbers from 0 to 9
}
}
在好的例子中,使用了一致的缩进(4 个空格),使代码块清晰易懂。在坏的例子中,缺少缩进使得代码结构难以看清,从而导致错误和误解。
Go 有一个名为 gofmt 的内置工具,可以根据语言的样式指南自动格式化 Go 代码,确保缩进和空格的一致性
34 拆分长行以提高可读性
分解长行代码可以提高可读性,使复杂的表达式更容易理解。
长行代码可能难以阅读和理解。将它们分解成更小、更易于管理的部分有助于保持清晰度和可读性。
Good
package main
import "fmt"
func main() {
// Breaking up a long line for better readability
message := "This is a very long string that needs to be broken up" +
"into multiple lines to improve readability."
fmt.Println(message)
}
Bad
package main
import "fmt"
func main() {
// A long line that is hard to read
message := "This is a very long string that needs to be broken up into multiple lines to improve readability."
fmt.Println(message)
}
在好的例子中,长字符串使用 + 运算符分成多行,使其更易于阅读和理解。在坏的例子中,长行难以阅读,并且可能让人不知所措,尤其是在更复杂的代码中。(也可以使用 `` 对字符串进行拆分)
Go 的 gofmt 工具还可以帮助分解长行代码,确保代码符合语言的风格指南并保持可读性。
35 使用空行分隔代码的逻辑部分
使用空行分隔代码的逻辑部分可以提高可读性并帮助其他人理解程序的结构和流程。
Good
package main
import "fmt"
func main() {
// Variable declarations
var a int = 10
var b int = 20
// Perform addition
sum := a + b
// Print the result
fmt.Println("Sum:", sum)
}
Bad
package main
import "fmt"
func main() {
var a int = 10
var b int = 20
sum := a + b
fmt.Println("Sum:", sum)
}
在好的例子中,空行用于分隔变量声明、加法运算和打印语句。这使得代码更易于阅读和理解。在坏的例子中,所有代码都挤在一起,使得逻辑更难理解。
在许多编程语言中,不只是 Go,使用空行也是一种常见的做法。它有助于保持代码库的整洁和有序,这在协作项目中尤为重要。
36 将行长度限制在合理的最大值内
将行长度限制在合理的最大值内可确保代码无需水平滚动即可轻松阅读,并且非常适合代码审查工具。
将行长度保持在合理的范围内(通常为 80-100 个字符)有助于保持可读性并确保代码在不同的设备和编辑器上都能很好地显示。
Good
package main
import "fmt"
func main() {
// This line is within the 80-character limit
fmt.Println("This is a reasonably long line that is still easy to read.")
}
Bad
package main
import "fmt"
func main() {
// This line is way too long and exceeds the typical 80-100 character limit, making it hard to read and manage in most editors and tools.
fmt.Println("This is an excessively long line that goes beyond the typical character limit, making it difficult to read and manage.")
}
在好的例子中,行长保持在合理的范围内,使其易于阅读和管理。在坏的例子中,行过长,这会导致可读性问题,并且可能需要在某些编辑器中水平滚动。
许多代码编辑器和 ID 都有可以突出显示或强制执行行长度限制的设置或插件,帮助开发人员维护一致且可读的代码。
37 对变量和函数使用一致的命名约定
一致的命名约定提高了代码的可读性和可维护性。
使用一致的命名约定有助于其他开发人员快速理解变量和函数的用途。
Good
// Good example of consistent naming conventions
package main
import "fmt"
// CalculateSum adds two integers and returns the result
func CalculateSum(a int, b int) int {
return a + b
}
func main() {=
firstNumber := 5
secondNumber := 10
result := CalculateSum(firstNumber, secondNumber)
fmt.Println("Sum:", result)
}
Bad
// Bad example with inconsistent naming conventions
package main
import "fmt"
// calc adds two integers and returns the result
func calc(x int, y int) int {
return x + y
}
func main() {
num1 := 5
num2 := 10
res := calc(num1, num2)
fmt.Println("Sum:", res)
}
在好的例子中,函数名 CalculateSum 清楚地描述了其用途,变量名 firstNumber 和 secondNumber 也具有描述性。在坏的例子中,函数名 calc 含糊不清,变量名 num1、num2 和 res 不具有描述性,使得代码更难理解。
Go 的命名约定通常使用驼峰式命名法来命名变量和函数。导出的名称(以大写字母开头的名称)可从其他包访问。
38 避免深度嵌套的代码块
深度嵌套的代码块会使代码难以阅读和维护。
减少嵌套代码块的深度可以提高可读性并使代码更容易理解。
Good
func main() {
numbers := []int{1, 2, 3, 4, 5}
for _, number := range numbers {
if number % 2 == 0 {
fmt.Println(number, "is even")
continue
}
fmt.Println(number, "is odd")
}
}
Bad
func main() {
numbers := []int{1, 2, 3, 4, 5}
for _, number := range numbers {
if number % 2 == 0 {
if number > 0 {
fmt.Println(number, "is even")
}
} else {
if number > 0 {
fmt.Println(number, "is odd")
}
}
}
}
在好的例子中,使用 continue 有助于提前处理偶数情况,从而避免额外的嵌套。坏的例子有多层嵌套的 if 语句,使其更难阅读和理解。
Go 鼓励使用提前返回(先处理特殊情况快速返回,一般称为 fast path)和 continue 语句来减少嵌套并提高代码清晰度。
39 使用空行分隔代码的逻辑部分
使用空行分隔代码的逻辑部分可以提高可读性,并帮助其他人理解程序的结构和流程。
空行可用于视觉上分隔代码的不同部分,例如变量声明、函数定义和函数内的不同逻辑块。
和 35 条重复了。
40 注释和文档使用一致的格式
注释和文档的一致格式确保您的代码易于理解和维护。
使用一致的注释和文档风格有助于其他开发人员快速掌握代码的目的和功能。
Good
// main is the entry point of the program
func main() {
// Variable declarations
var a int = 10
var b int = 20
// Perform addtion
sum := a + b
// Print the result
fmt.Println("Sum:", sum)
}
Bad
// main is the entry point of the program
func main() {
// Variable declarations
var a int = 10
var b int = 20
// perform addition
sum := a + b
//Print the result
fmt.Println("Sum:", sum)
}
在好的例子中,注释的格式一致,在后面有一个空格,并在适当的位置放在单独的行上。这使得注释易于阅读和理解。在坏的例子中,注释的位置和格式不一致使它们更难阅读,并可能导致混淆。
Go 有一个名为 gofmt 的工具,可以根据语言的样式指南自动格式化 Go 代码,其中包括正确的注释格式。使用 gofmt 可以帮助保持代码库的一致性。