美文网首页
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