GO编程规范

GO 编程规范

注释

可以通过\* ... \*或者//注释, //之后应该有个空格。 如果想在每个文件的头部加上注释,需要在版权注释和Package前面加一个空行,否则版权注释会作为package的注释 

// Copyright 2020 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package os

 注释:注释应该用一个完整的句子,注释的第一个单词应该是要注释的提示符,以便在godoc中容易查找;注释应该以.结尾。

  • 对外(Public首字母大写)的结构体、函数和包必须进行注释
  • 结构体注释格式:
// ObjectMeta is metadata that all persisted resources must have, which includes all objects
// users must create.
type ObjectMeta struct {
}
  • 函数注释格式:
// Compile parses a regular expression and returns, if successful,
// a Regexp that can be used to match against text.
func Compile(str string) (*Regexp, error) {
    。。。
}
  • 包注释格式:
// Package path implements utility routines for
// manipulating slash-separated filename paths.
package path

import

对 import 的包进行分组管理,用换行符分割,而且标准库作为分组的第一组。如果你的包引入了三种类型的包,标准库包,程序内部包,第三方包,建议采用如下方式进行组织你的包。

package main
 
import (
    "fmt"
    "os"
 
    "kmg/a"
    "kmg/b"
 
    "code.google.com/a"
    "github.com/b"
    . "github.com/smartystreets/goconvey/convey"
    _ "github.com/ziutek/mymysql/godrv"
)
  • 前面加.可以不使用包名前缀直接使用里面的函数
  • 前面加_,主要是为了使用包的init函数,一般用在数据库方面的包中
  • 在项目中不要使用相对路径引入包:
// 错误示例
import "../net"
 
// 正确的做法
import "github.com/repo/proj/src/net"

依赖包统一用govendor管理

包名和文件名

包名应该使用单数形式,比如util,model,而不是utils,models;

  • 包名命名规则:包名应该为小写单词,不要使用下划线或者混合大小写,如main、person。
  • 文件夹命名规则:小写单词,使用横杠连接,如user-amin。
  • 文件命名 规则:小写单词,使用下划线连接,测试文件_test.go结束,如user_manager.go、user_test.go。
  • 一般情况下包名和文件夹命名是一致的,不过也可以不一样

方法名称

驼峰式命名,名字可以长但是得把功能,必要的参数描述清楚,函数名应当是动词或动词短语,不可导出的函数以小写开头。如 postPayment、deletePage、save。并依 Javabean 标准加上 get、set、is 前缀。例如:xxx + With + 需要的参数名 + And + 需要的参数名 + …..

Receiver的名称应该缩写,一般使用一个或两个字符作为Receiver的名称,如: 

func (f foo) method {
 ...
}

函数采用命名的多值返回,传入变量和返回变量以小写字母开头,如:

func nextInt(b []byte, pos int) (value, nextPos int)

函数返回值可能为空或零值时,最好加一个逻辑判断的返回值,如:

func Foo(a int, b int) (string, bool)

函数返回用显式,不要用隐式,避免返回值被重复定义,导致返回值错误,特别是error返回值,如:

func Foo() (bar *Bar,err error){
  a,err:=A()
  if err !=  nil{
      return
  }
  ...
  return
}
 
约定下面的写法
func Foo() (bar *Bar,err error){
  a,err:=A()
  if err !=  nil{
      return nil,err
  }
  ...
  return bar,nil
}

单个函数的接口名以"er"作为后缀,如Reader,Writer

接口的实现则去掉“er”,如:

type Reader interface {
    Read(p []byte) (n int, err error)
}

两个函数的接口名综合两个函数名,如:

type WriteFlusher interface {
    Write([]byte) (int, error)
    Flush() error
}

三个以上函数的接口名,类似于结构体名,如:

type Car interface {
    Start([]byte)
    Stop() error
    Recover()
}

可见性规则

Go语言中,使用大小写来决定该常量、变量、类型、接口、结构或函数是否可以被外部包调用来决定 根据约定,函数名首字母小写即为private, 函数名首字母大写即可public

字符串大小写

错误字符串不应该大写,应写成: 

fmt.Errorf("failed to write data.")

 注:缩写词必须保持一致,比如都大写URL或小写url;常量一般声明为MaxLength,而不是以下划线分割MAX_LENGTH或者MAXLENGTH;

处理error而不是panic或者忽略

为了代表的强健性,不要使用_忽略错误,而是要处理每一个错误,尽管代码写起来有些繁琐也不要忽略错误; 尽量不要使用panic;不需要标点结尾;在逻辑处理中不要使用panic,且业务逻辑中要有recover机制。

采用独立的错误流进行处理,如:

if err != nil {
    // error handling
    return // or continue, etc.
}
// normal code

常量

常量均需使用全部大写字母组成,并使用下划线分词:

const APP_VER = "1.0"

如果是枚举类型的常量,需要先创建相应类型:

type Scheme string
const (
    HTTP  Scheme = "http"
    HTTPS Scheme = "https"
)

如果模块的功能较为复杂、常量名称容易混淆的情况下,为了更好地区分枚举类型,可以使用完整的前缀:

type PullRequestStatus int
const (
    PULL_REQUEST_STATUS_CONFLICT PullRequestStatus = iota
    PULL_REQUEST_STATUS_CHECKING
    PULL_REQUEST_STATUS_MERGEABLE
)

变量

变量名称一般遵循驼峰法,但遇到特有名词时,需要遵循以下规则:

  • 如果变量为私有,且特有名词为首个单词,则使用小写,如 apiClient
  • 其它情况都应当使用该名词原有的写法,如 APIClient、repoID、UserID
  • 错误示例:UrlArray,应该写成 urlArray 或者 URLArray 若变量类型为 bool 类型,则名称应以 Has, Is, Can 或 Allow 开头:
var isExist boolvar hasConflict bool
var canManage bool
var allowGitHook bool

多个变量声明放在一起

var (
    isExist bool
    count int
)

在函数外部声明使用var,不要采用:=

长度约定

  • 代码块长度,比如约定超过10行就需要考虑优化。
  • 行代码长度控制,太长的需换行,提高代码可读性。

init

  • 在同一个文件中,可以重复定义init方法
  • 在同一个文件中,多个init方法按照在代码中编写的顺序依次执行
  • 在同一个package中,可以多个文件中定义init方法
  • 在同一个package中,不同文件中的init方法的执行按照文件名先后执行各个文件中的init方法

建议同一个文件中只定义一个init方法,同一个package中init尽量合并

经验和建议

if

if接受初始化语句,约定如下方式建立局部变量

if err := file.Chmod(0664); err != nil {
  return err
}

注意if代码块的同名变量遮盖问题

for

for 采用短声明建立局部变量

sum := 0
for i := 0; i < 10; i++ {
  sum += i
}

range

如果只需要第一项(key),就丢弃第二个:

for key := range m {
    if key.expired() {
        delete(m, key)
    }
}

如果只需要第二项,则把第一项置为下划线

sum := 0
for _, value := range array {
    sum += value
}

return

尽早 return:一旦有错误发生,马上返回

f, err := os.Open(name)
if err != nil {
  return err
}
d, err := f.Stat()
if err != nil {
  f.Close()
  return err
}
codeUsing(f, d)

struct

声明和初始化采用多行,初始化结构体使用带有标签的语法

type User struct{
    Username  string
    Email     string
}
 
u := User{
    Username: "yourname",
    Email:    "yourname@gmail.com",
}

修改对象属性不能直接使用赋值,要写成方法且必须加锁

map

非线程安全,并发读写map的情况下必须加锁,不然会产生panic go 1.9以下版本可参考 beego的safemap,1.9以上版本使用sync.Map

空字符串检查

if s == "" {
	...
}

非空slice检查

if len(s) > 0 {
	...
}

byte/slice/string相等性比较

var s1 []byte
var s2 []byte

...

bytes.Equal(s1,s2) == 0
bytes.Equal(s1,s2) != 0

检查是否包含子字符串

应使用strings.ContainesRune, strings.ContainesAny, strings.Contains

复制slice

使用内建函数copy,而不是遍历slice逐个复制:

var b1,b2 []byte
copy(b2,b1)

参数传递

  • 对于少量数据,不要传递指针
  • 对于大量数据的 struct 可以考虑使用指针
  • 传入的参数是 map,slice,chan 不要传递指针,因为 map,slice,chan 是引用类型,不需要传递指针的指针

单元测试

单元测试文件名命名规范: example_test.go

测试用例的函数名称必须以 Test 开头,例如:

func TestExample(t * testing.T)

性能测试函数名以Benchmark开头

func BenchmarkExample(b *testing.B)

其他工具

  • go fmt 自动格式化代码
  • go vet 静态分析我们的源码存在的各种问题
  • go doc 查看注释信息
  • go build 执行构建
  • go install 将构建成功的包安装到恰当的位置
  • go test 执行测试
  • go test -coverprofile=c.out 代码覆盖率
  • go test-test.bench 执行性能测试
  • go test -run=xxx -bench=. -benchtime="3s" -cpuprofile profile_cpu.out 运行性能测试,输出profile文件
  • go tool pprof app.test profile_cpu.out 查看profile文件
  • go tool pprof -svg profile_cpu.out > profile_cpu.svg 性能分析生产svg
  • go tool pprof -pdf profile_cpu.out > profile_cpu.pdf 性能分析生产pdf 需要安装graphviz
  • go build -buildmode=c-shared -o exportgo.dll exportgo.go 编译成dll

常用包

  • bufio==>实现缓冲的I/O
  • bytes==>提供对字节切片的操作函数
  • crypto==>收集场景的加密常数
  • errors==>实现了操作错误的函数
  • Expvar==>为公共变量提供一个标准的接口,如服务器中的运算计数器
  • flag==>实现了命令行标记解析
  • fmt==>实现了格式化输入输出
  • hash==>提供了哈希函数接口
  • html==>提供了一个HTML5兼容的分词器和解析器
  • image==>提供了一个基于二维图形的图像库
  • io==>提供了I/O原语的基本接口
  • log==>他是一个简单的记录包,提供基本的日志功能
  • math==>提供了一些基本的常量和数学函数
  • mine==>实现了部分MIME规范
  • net==>提供了一个对UNIX网络套接字的可移植接口,包括TCP/IP、UDP域名解析和UNIX域套接字
  • os==>为操作系统功能实现了一个平台无关的接口
  • path==>实现了对斜线分隔分的文件名路径的操作
  • refect==>实现运行时反射,运行一个程序以任意类型操作对象
  • regexp==>实现一个简单的正则表达式库
  • runtime==>包含域Go运行时系统交互的操作,如控制goroutine的函数
  • sort==>提供对集合排序的基础函数
  • strconv==>实现了在基本数据类和字符串之间的转换
  • strings==>实现操作字符串的简单函数
  • sync==>提供了基本的同步机制,如互质锁
  • testing==>提供对自动测试包的功能
  • time==>提供时间相关的包
  • unicode==>Unicode编码相关的基础函数
  • archive/tar==>实现对tar压缩文档的访问
  • archive/zip==>提供对ZIP压缩文档的读和写支持
  • container/heap==>提供了实现heap.Interface接口的任何类型的堆操作
  • container/list==>实现了一个双链表
  • container/ring==>实现了对循环链表的操作

其他

defer

defer的执行方式类似其他语言的析构函数,在函数体执行结束后按照调用顺序的相反顺序逐个调用 即使函数发生严重错误也会执行 参考:d3 defer_test.go

  • 常用于资源清理,文件关闭,解锁以及记录时间等操作
  • 通过与匿名函数配合可以在return之后修改函数计算结果
  • 如果函数体内某个变量作为defer时匿名函数的参数,则在定义defer时即已经获得了拷贝,否则则是引用某个变量的地址

chan

  • 有buffer的channel,要注意“放”先于“取”
  • 无buffer的channel,要注意“取”先于“放”
  • 如果给一个 nil 的 channel 发送或者接收数据,会造成永远阻塞。
  • 18
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Google Go编程规范是一套由谷歌公司推荐的Go语言编程规范指南。这些规范旨在提高代码的可读性、可维护性和可扩展性,并促进Go语言代码的一致性。 Google Go编程规范的主要内容包括以下几个方面: 1. 代码布局:Go代码的文件布局应当清晰简洁,采用一定的结构组织,例如定义包名、导入声明、变量和常量声明的位置等。 2. 命名规范:Go代码中的变量、函数、常量和类型的命名应当具有清晰的含义和一致的命名风格,使用camelCase命名法,并避免使用缩写和简写。 3. 注释规范:Go代码应当包含适当的注释,对关键代码段进行解释和说明,有助于其他开发人员理解和维护代码。注释应当使用英文编写,主要包括包注释、函数注释和类型注释等。 4. 格式化规范:Go代码应当采用一定的格式化风格,例如使用tab而不是空格缩进、行宽限制等。可以使用Go语言自带的工具go fmt来自动格式化代码。 5. 错误处理:Go代码应当对可能发生的错误进行有效的处理,避免简单地忽略错误或者将其抛给调用者。可以通过返回错误值或者使用defer语句执行清理操作。 6. 并发性:Go代码中的并发操作应当谨慎处理,避免出现资源竞争和死锁的情况。可以通过使用互斥锁、通道等机制来保证并发安全。 综上所述,Google Go编程规范提供了一系列的指导原则,帮助开发人员编写高质量的Go代码。遵循这些规范可以提高代码的可读性和可维护性,使得Go语言项目更加规范和易于理解与扩展。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值