美文网首页
Go编码规范

Go编码规范

作者: 无心Y | 来源:发表于2023-08-19 16:40 被阅读0次

    命名规范

    文件名

    • 小写单词,使用下划线分割,简短有意义
    base_test.go
    
    

    包命名

    • 包名与目录名相同,小写单词,不要使用下划线,不要和标准库冲突
    package main
    
    package utils
    
    

    结构体

    • 驼峰式命名,首字母根据访问控制大小写
    type User struct {
        Name   string
        Age    uint32
        Gender uint8
    }
    
    

    接口名

    • 单个方法,接口以er结尾
    interface Reader {
        Read(content []byte) (n int, err error)
    }
    
    
    • 两个方法,接口综合两个方法名
    interface ReadWriter {
        Read(content []byte) (n int, err error)
        Write(content []byte) (n int, err error)
    }
    
    
    • 三个以上的方法,接口命名同结构体
    type Car interface {
        Start([]byte)
        Stop() error
        Recover()
    }
    
    

    函数名

    • 函数/方法名称应该直接表明函数的用途

    • 对于Seter和Geter的命名方式,Owner, SetOwner

    type Pen struct{
        Length  float64
        owner   string
    }
    
    
    (p *Pen) func Owner() string {
        return p.owner
    }
    
    (p *Pen) func SetOwner(owner string) {
        p.owner = owner
    }
    
    
    • 对于判断类型的函数,以Has, Is, Can, Allow开头
    func HasPrefix(name string, prefixes []string) bool { ... }
    func IsEntry(name string, entries []string) bool { ... }
    func CanManage(name string) bool { ... }
    func AllowGitHook() bool { ... }
    
    

    常量名

    • 全部大写,用下划线分割
    
    const APP_VERSION = "1.0"
    
    
    • 枚举类型的常量,需要先创建类型
    type Protocol string
    
    const (
        HTTP  Protocol = "http"
        HTTPS Protocol = "https"
    )
    
    
    • 功能较复杂的情况下,常量名容易混淆,可以使用完整的前缀
    type PullRequestStatus int
    
    const (
        PULL_REQUEST_STATUS_CONFLICT PullRequestStatus = iota
        PULL_REQUEST_STATUS_CHECKING
        PULL_REQUEST_STATUS_MERGEABLE
    )
    
    

    变量名

    • 全局变量,驼峰式命名
    var (
        ProjectName  string
        ProjectOwner string
    )
    
    
    • 局部变量,驼峰式命名,使用短变量赋值
    projectName  := "venus"
    projectOwner := "loda"
    
    
    • 变量缩写

      • user可以简写为 u
      • userId可简写为 uid
      • context可简写为 ctx
      • error可以简写为 err
      • ...
    • bool类型变量,以Is, Has, Can, Allow等开头

    注释规范

    注释风格

    • 全部使用单行注释

    • 单行注释不要太长,不能超过120个字符

    包注释

    • 位于package之前

    • 包的基本简介

    • 创建者

    • 创建时间

    // util 包含一些公用的函数
    // Author: loda
    // Date: 20210723
    
    package util
    
    

    结构体(接口)注释

    • 结构体名称,结构体说明
    // User , 用户对象,定义了用户的基础信息
    type User struct{
        Username  string // 用户名
        Email     string // 邮箱
    }
    
    

    函数(方法)注释

    • Title, Description, Author, Params, Return
    // @Title NewtAttrModel 
    // @Description 属性数据层操作类的工厂方法
    // @Author loda
    // @Param  ctx  上下文信息
    // @Return 属性操作类指针
    func NewAttrModel(ctx *common.Context) *AttrModel {
    }
    
    

    代码逻辑注释

    • 关键位置的代码逻辑

    • 局部较复杂的逻辑说明

    Bug注释

    • 针对代码中出现的Bug
    // BUG(astaxie):This divides by zero. 
    var i float = 1/0
    
    

    代码风格

    缩进和换行

    • 缩进使用gofmt工具即可

    • 一行不超过120个字符,超过换行显示

    控制结构

    • 语句的结尾

      • 不需要分号,默认一行就是一句
      • 可以显示使用分号连接两行,但不建议使用
    • 括号和空格

      • 左大括号不换行
      • 所有的运算符和操作数之间要有空格
    • if else

      • 条件语句不需要小括号
      • 省略不必要的else
      • 可加上合适的初始化语句
    result := query()
    
    if err := check(result); err != nil {
        return err
    }
    
    
    • for
    
    for i:=0; i++; i<10 {
        // do sth
    }
    
    
    • 遍历
    import fmt
    
    for pos, str := range "hello" {
        fmt.Printf("%q: %d\n", str, pos)
    }
    
    // 遍历 array, slice, map, chan, string
    for k, v := range mapA {
        // do sth
    }
    
    
    • switch
      • 表达式可以为变量、常量等
      • case可以列举多个条件
      • 可使用break体检结束
    func Factory(name string, value interface{}) interface{} {
        var object interface{}
    
        switch name {
        case "A1", "A2":
            object = NewA()
        case "B":
            object = newB()
            if value == nil {
                break
                }
            object.SetValue(value)
        }
    
        return object
    }
    
    
    • switch type
    func ErrorWrap(e interface{}) *TraceableError {
        var message string
    
        switch e := e.(type) {
        case TraceableError:
            return &e
        case *TraceableError:
            return e
        case error:
            message = e.Error()
        default:
            message = fmt.Sprintf("%v", e)
        }
        return ErrorNew(message, 2)
    }
    
    
    • break
      • break在switch中可以提前结束
      • break在循环中需要借助标签提前结束
    package main
    
    import (
        "fmt"
    )
    
    func main() {
    Loop:
        for index := 1; index < 10; index++ {
            switch index % 5 {
            case 1:
                break 
            case 0:
                break Loop
            default:
                fmt.Printf("%v\n", index)
            } 
        }
    }
    
    
    • select类似switch, 用于多个管道的读取

    结构体和接口

    • 结构体初始化
    type MyStruct struct {
        Value int
    }
    
    type MyStruct2 struct {
        MyStruct
        ID int 
    }
    
    p1 := new (MyStruct) // type *SyncedBuffer
    p2 := &MyStruct{}   // type *SyncedBuffer
    
    s1 := MyStruct{
        Value: 0, 
    }
    
    s2 := MyStruct2{
        ID: 0,
        MyStruct1: MyStruct1 {
            Value: 1, 
        },
    }
    
    

    method receiver

    • map, chan, interface, func: 不要使用指针(它们隐式使用指针)

    • 如果不存在对切片重新分配,则不要使用指针

    • 如果方法会改变receiver,则使用指针

    • 如果receiver中有类似sync.Mutex等用于同步的成员,则必须使用指针

    • 一般接受者都声明为指针类型

    defer

    • 打开文件、连接等后,需要defer关闭连接
    • 慎用defer处理锁

    chan

    • 创建管道时使用make

    • 只读管道: ch <-chan int

    • 只写管道: ch chan<- int

    
    ch := make(chan int 1)
    
    func handle(readCh <-chan int, writeCh chan<- int) {
        go func() {
            v := <-readCh
            writeCh <- 2 * v
        }()
    }
    
    

    gorouting

    • 线程安全

      • gorouting是在线程池中执行的,在gorouting中访问闭包需要考虑线程安全问题
    • Once

      • 提供一个线程安全的单次执行的接口,用于单例模式或初始化场景
    package main
    
    import (
        "sync"
    )
    
    type singleton struct {}
    
    var instance *singleton
    
    var once sync.Once
    
    func GetInstance() *singleton {
        once.Do(func() {
            instance = &singleton{}
        })
        return instance
    }
    
    
    • WaitGroup
      • 初始化 WaitGroup,加上特定的值
      • goroutine结束时记得调用WaitGroup.Done()
      • 主流程执行 WaitGroup.Wait()
    package main
    
    import (
        "fmt"
        "sync"
    )
    
    func main() {
        var wg sync.WaitGroup
    
        wg.Add(1)
    
        go func() {
                fmt.Printf("hello ")
                wg.Done()
            }()
    
        wg.Wait()
        fmt.Printf("world\n")
    }
    
    
    • Atomic
      • sync.atomic包提供了一系列同步原语来控制临界资源的访问
    package main
    
    import (
        "fmt"
        "sync"
        "sync/atomic"
    )
    
    func main() {
        var value int64
        var wg sync.WaitGroup
    
        wg.Add(2)
    
        fun := func(count int) {
            for index := 0; index < count; index++ {
                atomic.AddInt64(&value, 1)
            }
            wg.Done()
        }
    
        go fun(100)
        go fun(100)
    
        wg.Wait()
        fmt.Printf("%v\n", value)
    }
    
    

    import规范

    • 依次导入标准包、内部包、第三方包

    • 不要使用相对路径

    import (
        "encoding/json"  //标准包
        "strings"
    
        "myproject/models"
        "myproject/controller"   //内部包
        "myproject/utils"
    
        "github.com/astaxie/beego"   //第三方包
        "github.com/go-sql-driver/mysql"
    ) 
    
    

    错误处理

    • 不要使用'_'丢弃任何有返回err的调用

    • 一旦有错误发生,尽早return

    • 采用独立的错误流进行处理

    • 尽量不要使用panic

    // 错误写法
    if err != nil {
        // error handling
    } else {
        // normal code
    }
    
    // 正确写法
    if err != nil {
        // error handling
        return // or continue, etc.
    }
    // normal code
    
    

    参数传递

    • 对于少量数据不要使用指针

    • 对于大量数据的struct可以考虑使用指针

    • 传入参数是map,slice,chan不要传递指针,因为map,slice,chan是引用类型,不需要传递指针的指针

    单元测试

    • 单元测试代码的go文件必须以_test.go结尾

    • 单元测试的函数名必须以Test开头,是可导出公开的函数

    • 测试函数的签名必须接收一个指向testing.T类型的指针作为参数,并且该测试函数不能返回任何值

    相关文章

      网友评论

          本文标题:Go编码规范

          本文链接:https://www.haomeiwen.com/subject/sdujmdtx.html