Go语言开发规范

Go 语言由于其高性能的特性,一般在服务器端使用。比如 Linux 资源管理,分布式计算等。开发规范强调代码简洁、易读、并且有良好的风格一致性。

1. 代码风格

  • 缩进: 使用 tabs(而不是 spaces)进行代码缩进。
  • 行宽: 代码行尽量不要超过 80 个字符。
  • 空行: 合理使用空行来分隔不同功能块或函数。
  • 命名规范:
    • 使用 驼峰命名法(camelCase)来命名变量、函数和字段名。
    • 使用 PascalCase(大驼峰)来命名类型、结构体(struct)、接口(interface)等。
    • 常量使用大写字母与下划线(UPPER_CASE)进行命名。
    • 避免使用单字母命名,除非是非常常见的(例如:i, j, k 在循环中)。
    • 保持命名简洁明了,避免不必要的缩写。

2. 文件和目录结构

  • 主程序入口: 应将 main 包放在项目的根目录中,并且保持其尽量简单。
  • 目录结构: 可以使用如下的结构来组织项目:
    /cmd          - 主程序
    /pkg          - 可重用的库
    /internal     - 私有库,不能被外部引用
    /api          - 接口定义
    /web          - 网页、模板等资源
    /scripts      - 脚本文件
    /docs         - 文档
    
  • 每个包的职责单一: 每个包只处理一个特定的任务,不要让一个包承担过多的功能。

3. 错误处理

  • Go 没有异常机制,错误处理依赖于 error 类型。
    • 函数返回错误时,通常需要显式检查错误。
    • 处理错误时尽量提供清晰的错误信息,以便于调试。
    • 错误检查的惯例是:
      result, err := someFunction()
      if err != nil {
          log.Fatal(err)  // 或者返回错误
      }
      

4. 并发编程

  • goroutine: 用来实现并发的基本单元。使用时要注意避免 goroutine 泄漏。
  • channel: 用来在 goroutine 之间传递数据,推荐使用具名的 channel 类型来提高代码可读性。
  • select 语句: 用于选择多个 channel 操作,避免阻塞。
  • 保持共享数据的同步性,尽量避免直接共享内存,使用通道(channel)进行通信。

5. 接口和类型

  • 接口(interface):
    • Go 语言的接口是隐式实现的,即不需要显式声明一个类型实现了某个接口。
    • 接口的设计应保持精简和聚焦。
    • 接口方法尽量不要带有实现,避免不必要的耦合。
  • 结构体(struct):
    • 使用结构体时,字段最好使用导出字段(大写字母开头)。
    • 如果一个结构体有多个字段,使用行内注释来描述字段的作用。
    • 如果结构体字段有默认值,可以通过构造函数来初始化。

6. 文档和注释

  • 文档注释:函数、类型、方法和包应有清晰的文档注释,注释应该是完整的句子,并且描述其作用。
  • 函数和方法注释:函数的注释应直接位于函数声明前。
    // CalculateSum computes the sum of two integers.
    func CalculateSum(a, b int) int {
        return a + b
    }
    

7. 包管理

  • 使用 Go modules 来管理包依赖。避免使用 GOPATH,将项目与外部包的管理交给 Go modules。
    • 使用 go mod init 创建一个模块。
    • 使用 go mod tidy 清理未使用的依赖。
    • 使用 go get 安装外部依赖。

8. 性能和优化

  • 避免不必要的内存分配:使用 sync.Pool 来重用对象,避免频繁的内存分配。
  • 延迟加载:在需要时再加载资源而不是提前加载。
  • 性能测试:通过 testing 包进行性能测试,使用 go test -bench 来测试性能。

9. 单元测试

  • Go 提供了 testing 包来编写单元测试。
    • 测试函数名通常以 Test 开头。
    • 编写清晰的测试,检查边界条件和常规情况。
    • 使用 go test 运行测试。

10. 依赖注入

  • Go 语言中并没有内建的依赖注入框架。通常,依赖注入通过构造函数进行:

    type Service struct {
        repo Repository
    }
    
    func NewService(r Repository) *Service {
        return &Service{repo: r}
    }