文章详情页 您现在的位置是:网站首页>文章详情

Golang编码规范

图片丢失 Jeyrce.Lu 发表于:2021年6月19日 16:45 分类:【Go 1983次阅读

本文主要结合 uber的go代码风格和官方一些经验整理,有删改。

一、命名规范

(0)关键字

break
defaultfuncinterface
select
case
defergomapstruct
chanelsegotopackage
switch
const
fallthroughif
rangetype
continue
forimportreturnvar

所有小写命名应当避免和关键字相同,用大写开头或添加下划线区分。

// 不推荐的写法
var case string

// 推荐的写法
var Case
var _case
var case_

(1)包名

  • 保持名称和dir一致(main包除外)

  • 简短有意义

  • 尽量不合标准库冲突

  • 全部为小写单词(不要使用下划线、大小写混合)

// 一般写法
package User    // 含有大写
package good_lucky_user // 含有下划线,且命名太过冗杂
package  os  // 和标准库冲突

// 推荐的写法
package user
package xxxos


(2)文件名

  • 简短有意义

  • 全部为小写单词

  • 下划线分割单词

// 一般写法
style/model/
├── simple_healthy_fantistic_task.go
└── Task.go

// 推荐写法
style/model/
├── task.go
└── task_test.go


(3)结构体名

  • 驼峰命名法,大小写控制私有和共有

  • 声明和初始化每个字段占用一行

  • 不包含包名

// 一般写法
package model
 
// 不符合驼峰命名法
type file_parser struct {
    Path, Size string   // 两个字段放在一行
    Count int
}
 
// 包名已经有model
type ModelJob struct {
    ...
}

// 推荐写法
package model
 
type fileParser struct {
    Path string
    Size string
    Count int
}
 
type Job struct {
    ...
}


(4)接口名

  • 同结构体命名规范

  • 单函数接口尽量采用"er"作为后缀,例如标准库中的“Reader、Writer”

// 一般写法
type Read interface {
    Read(string) ([]byte, error)
}
 
type Write interface {
    Write(interface{}, string) error
}

// 推荐写法
type Reader interface {
    Read(string) ([]byte, error)
}
 
type Writer interface {
    Write(interface{}, string) error
}


(5)变量名

  • 驼峰命名法

  • 缩写单词全部保持大写或者小写

  • bool类型推荐使用“Has、Is、Can、Allow”开头

// 一般写法
var user_email  // 下环线连接
var Api    // api是一个缩写单词,但是采用大小写混合
var IfRunning bool  // 随意命名

// 推荐写法
var userEmail
var API
var api
var IsRunning bool


(6)常量名

常量无需全部大写、下划线分词,遵循驼峰命名即可

// 一般写法
const LINK_PATH = "https://www.woqutech.com"

// 推荐写法
const LinkPath "https://www.wouqtech.com"


二、注释

(0)注释方式的选用

  • 块注释  /*注释*/

  • 行注释 // 注释

一般情况下无需关心采用哪种注释方式,但是标准库以及各大开源项目最多使用//

(1)包注释

  • 出现在package语句之前

  • 包内如果有多个文件则在其中一个文件(一般和包名相同)写即可

(2)结构体(接口)注释

  • 每个结构体都应当有注释,放在结构体定义的前一行

  • 重要字段或者意义不明确字段应当有注释说明,位于字段后面对齐

// 一般写法
type Area struct{}  // 无注释
 
type User struct { // 用户模型  // 注释位置
    Name     string
    Password string
    Option   map[string]interface{}
}

// 推荐写法
// 区域数据模型
type Area struct{}
 
// 用户数据模型
type User struct {
    Name     string
    Password string
    Option   map[string]interface{} // 扩展字段,记录用户的一些额外属性
}


(3)函数(方法)注释

  • 每个函数都应当书写注释,放在函数声明前

  • 注释一般包含4方面

    • 简要功能说明

    • 参数列表

    • 返回值

    • 重要情况说明(此部分不是所有函数都需要)

      • 逻辑步骤

      • 注意点

      • 一些补充说明的参考链接

  • 关键的位置参数可以使用 /*参数意义*/来注释

(4)代码逻辑注释

  • 关键位置、复杂逻辑需要添加适当注释方便其他开发者理解

  • 全部采用中文注释(要对自己祖国的语言有信息,不要搞些花里胡哨的英文)

  • 同代码语句,尽量不超过120字符每行

三、代码风格

(0)避免panic

  • panic是一种严重错误,出现之后程序直接崩溃,因此需要尽量避免,除非你明白自己在做什么

  • 使用recover来捕获一些错误

(1)语句缩进、折行

  • 语句尽量不超过120字符,超过后使用\或者小括号折行

  • 使用table缩进

(2)导入

  • 导入多个包时需要遵循一定的顺序

    • 标准库

    • 三方包

    • 本地包

  • 不同类型的导入之间使用空格分开

  • 当导入的包携带版本或和其他包冲突,应当定义别名

// 一般写法
import (
    "database/sql/driver"
    "encoding/json"
    "fmt"
    "time"
    "sourcegraph.com/sqs/byte"
    "gopkg.in/yaml.v2"
    "github.com/woqutech/phoenix/discovery"
)


// 推荐写法
import (
    // 标准库中的包
    "fmt"
    "time"
    "database/sql/driver"
    "encoding/json"
 
    // 三方包
    pbyte "sourcegraph.com/sqs/byte"    // 和byte类型冲突,因此定义别名
    yaml "gopkg.in/yaml.v2" // 目录和包名不一致,因此定义别名
     
    // 自定义的包
    "github.com/woqutech/phoenix/discovery"
)


(3)错误处理

  • 接收到error后使用log记录或者返回

  • 尽量在程序中使用if err != nil 而不是 if err == nil,避免程序嵌套太深

  • 函数尽量都设计两个返回值,一个数据和一个error

// 一般写法
func CheckUser() (bool, error) {
    ...
    if err == nil {
        ...
        if err == nil {
            ...
        } else {
            ...
        }
    } else {
        ...
    }
}

// 推荐写法
func CheckUser() (bool, error) {
    ...
    if err != nil {
        ...
    }
    ...
    if err != nil{
        ...
    }
    ...
}


(4)资源管理

  • 能够使用defer的尽量使用defer管理,这样的操作通常成对出现

    • lock——unlock

    • open——close

  • defer就算在程序panic的时候依然会执行

  • 不要在for循环中使用defer

// 一般写法
func DoSomething()  {
    ...
    lock := resource.Lock()
    ...
    ...
    lock.UnLock()
}

// 推荐写法
func DoSomething()  {
    ...
    lock := resource.Lock()
    defer lock.UnLock()
    ...
    ...
}


(5)表格驱动测试

  • 为每一个功能性函数、结构体等创建测试

  • 测试case需要尽可能包括多种情况

  • 多种情况采用[]保存,range使用,不要一项一项罗列

// 一般写法
host, port, err := net.SplitHostPort("192.0.2.0:8000")
require.NoError(t, err)
assert.Equal(t, "192.0.2.0", host)
assert.Equal(t, "8000", port)
 
host, port, err = net.SplitHostPort("192.0.2.0:http")
require.NoError(t, err)
assert.Equal(t, "192.0.2.0", host)
assert.Equal(t, "http", port)
 
host, port, err = net.SplitHostPort(":8000")
require.NoError(t, err)
assert.Equal(t, "", host)
assert.Equal(t, "8000", port)
 
host, port, err = net.SplitHostPort("1:8")
require.NoError(t, err)
assert.Equal(t, "1", host)
assert.Equal(t, "8", port)

// 推荐写法
tests := []struct{
  give     string
  wantHost string
  wantPort string
}{
  {
    give:     "192.0.2.0:8000",
    wantHost: "192.0.2.0",
    wantPort: "8000",
  },
  {
    give:     "192.0.2.0:http",
    wantHost: "192.0.2.0",
    wantPort: "http",
  },
  {
    give:     ":8000",
    wantHost: "",
    wantPort: "8000",
  },
  {
    give:     "1:8",
    wantHost: "1",
    wantPort: "8",
  },
}
 
for _, tt := range tests {
  t.Run(tt.give, func(t *testing.T) {
    host, port, err := net.SplitHostPort(tt.give)
    require.NoError(t, err)
    assert.Equal(t, tt.wantHost, host)
    assert.Equal(t, tt.wantPort, port)
  })
}


(6)channel空间大小

  • 对于可能出现阻塞的地方需要为chan增加缓冲

    • 竞态条件

    • 通道边界

  • 缓冲的size要么为1,要么无缓冲

// 一般写法
// 缓冲区太大
c := make(chan int, 64)

// 推荐写法
// 大小:1
c := make(chan int, 1) // 或者
// 无缓冲 channel,大小为 0
c := make(chan int)


(7)指定容器容量

  • 使用make来初始化map和slice

  • 尽可能在初始化阶段指定容器长度,减少容器扩容带来的开销

// 一般写法
m := make(map[string]os.FileInfo)
 
files, _ := ioutil.ReadDir("./files")
for _, f := range files {
    m[f.Name()] = f
}

// 推荐写法
files, _ := ioutil.ReadDir("./files")
 
m := make(map[string]os.FileInfo, len(files))
for _, f := range files {
    m[f.Name()] = f
}


(8)尽量避免goto语句

尽量避免使用goto语句将程序逻辑变得复杂

(9)尽量少使用init方法

  • 避免隐式逻辑

  • 如果有一些巧妙的设计,则不排除使用init

golang-init.png

四、常用工具

(0)gofmt

(1)goimport

(2)go vet

五、检查工具集成

~待补充


版权声明 本文属于本站  原创作品,文章版权归本站及作者所有,请尊重作者的创作成果,转载、引用自觉附上本文永久地址: http://blog.lujianxin.com/x/art/mxp0cly2ksku

文章评论区

作者名片

图片丢失
  • 作者昵称:Jeyrce.Lu
  • 原创文章:61篇
  • 转载文章:3篇
  • 加入本站:2004天

站点信息

  • 运行天数:2005天
  • 累计访问:164169人次
  • 今日访问:0人次
  • 原创文章:69篇
  • 转载文章:4篇
  • 微信公众号:第一时间获取更新信息