网站首页> 文章专栏> Go代码规范梳理
Go代码规范梳理
日期:2022-01-26 20:00:17 作者:管理员 浏览量:3430

Go代码规范(Code Review)

总结几个开发过程中经常会犯的代码规范错误

参考链接:https://learnku.com/go/wikis/48375

注释语句

// Request 表示运行命令的请求。
type Request struct { ...

// Encode 将 req 的 JSON 编码写入 w 。
func Encode(w io.Writer, req *Request) { ...

上下文

大部分使用上下文的函数都要将其作为第一个参数

func F(ctx context.Context, /* 其他参数 */) {}

不建议把上下文作为成员添加到结构类型中

// 不建议写法
type MyContext struct {
    ctx context.Context
}

Crypto Rand

不要使用 math/rand 来生成密钥,即使是一次性密钥。在没有种子(seed)的情况下,生成器是完全可以被预测的。请使用 crypto/rand 的 Reader 作为替代

func Key() string {
	buf := make([]byte, 16)
	_, err := rand.Read(buf)
	if err != nil {
		panic(err) // 出于随机性,永远都不会发生
	}
	return fmt.Sprintf("%x", buf)
	// or hex.EncodeToString(buf)
	// or base64.StdEncoding.EncodeToString(buf)
}

func main() {
	fmt.Println(Key())
}
//输出
//4799b801e7866209593cfaedb03cf8da

声明空的切片

当声明一个空 slice 时,倾向于用

var t []string

而不是

t := []string{}

前者声明了一个 nil slice 值,而后者声明了一个非 nil 但是零长度的 slice。两者在功能上等同,len 和 cap 均为零,而 nil slice 是首选的风格。

请注意,在部分场景下,首选非 nil 但零长度的切片,例如编码 JSON 对象时(nil 切片编码为 null,而则 []string{} 可以正确编码为 JSON array [])。

包注释

/*
Package template implements data-driven templates for generating textual
output such as HTML.
....
*/
package template

如果包很简单,包注释可以很简短。

// Package math provides basic constants and mathematical functions.
package math

包名

对包中名称的所有引用都将使用包名完成,因此可以从标识符中省略该名称。 例如,如果你正在使用 chubby 包,则不需键入 ChubbyFile ,因为客户端会将其写为 chubby.ChubbyFile。 相反,命名为 File 的这种方式,客户端会将它写为 chubby.File 。 避免使用像 utilcommonmiscapitypesinterfaces 这样无意义的包名。

不要Panic

错误信息

即使用 fmt.Errorf("something bad") 而不要使用 fmt.Errorf("Something bad")

包的导入

避免包重命名导入,防止名称冲突;好的包名称不需要重命名。如果发生命名冲突,则更倾向于重命名最接近本地的包或特定于项目的包。

包导入按组进行组织,组与组之间有空行。标准库包始终位于第一组中。

package main

import (
    "fmt"
    "hash/adler32"
    "os"

    "appengine/foo"
    "appengine/user"

    "github.com/foo/bar"
    "rsc.io/goversion/version"
)

包的匿名导入

import _ "pkg" 这种导入语句,最好只存在于main.go文件中

Dot 导入

package foo_test

import (
    . "foo"
)

不要在程序中使用 import .。它将使程序更难阅读

内联错误

不规范写法

// 查找返回键的值,如果没有键的映射,则返回空字符串。
func Lookup(key string) string

规范写法

// 查找并返回键的值,如果没有键的映射,则ok = false。
func Lookup(key string) (value string, ok bool)

并有利于写出更健壮和可读性更强的代码:

value, ok := Lookup(key)
if !ok {
    return fmt.Errorf("no value for %q", key)
}
return Parse(value)

缩进错误处理

要缩进错误处理逻辑,不要缩进常规代码。这样可以改进代码的可读性,读者可以快速地浏览逻辑主干。例如,不要写:

if x, err := f(); err != nil {
    // 错误处理
    return
} else {
    // 使用变量 x
}

相反,应该这样写:

x, err := f()
if err != nil {
    // 错误处理
    return
}
// 使用变量 x

这样写的好处可以增加可读性

首字母缩写

名称中的单词是首字母或首字母缩略词(例如 "URL" 或 "NATO" )需要具有相同的大小写规则。例如,"URL" 应显示为 "URL" 或 "url" (如 "urlPony" 或 "URLPony" ),而不是 "Url"。举个例子:ServeHTTP 不是 ServeHttp。对于具有多个初始化 “单词” 的标识符,也应当显示为 "xmlHTTPRequest" 或 "XMLHTTPRequest"。

当 "ID" 是 "identifier" 的缩写时,此规则也适用于 "ID" ,因此请写 "appID" 而不是 "appId"。

protocol buffer 生成的代码是个例外,对人和对机器的要求不能一样,人编写的代码要比机器编写的代码保持更高的标准。

接口

总的来说,Go 的接口要包含在使用方的包里,不应该包含在实现方的包里。实现方只需要返回具体类型(通常是指针或结构体),这样一来可以将新方法添加到实现中,而不需要扩展重构。

不要在 API 的实现者端定义 "for mocking" 接口;反而是要定义公开的 API,用真实的实现进行测试。

不要先定义接口再用它。脱离真实的使用场景,我们都不能确定一个接口是否有存在的价值,更别提设计接口的方法了

建议写法,把接口和接口具体的实现类分开两个go文件

package consumer  // consumer_func.go

type Thinger interface { Thing() bool }

func Foo(t Thinger) string { … }
package consumer //consumer.go

type defaultThinger struct{ … }
func (t defaultThinger) Thing() bool { … }

func NewThinger() Thinger { return defaultThinger{ … } }

不规范写法

package producer

type Thinger struct{ … }
func (t Thinger) Thing() bool { … }

func NewThinger() Thinger { return Thinger{ … } }

方法接收者命名

方法接收者的名称应该反映其身份;通常,其类型的一个或两个字母缩写就足够了(例如用 "c" 或 "cl" 表示 "client" )。不要使用通用名称,例如 "me","this" 或 "self",这是面向对象语言的典型标识符,这些标识符赋予该方法特殊的含义。

方法接收者类型

要是把握不准,就用指针。

变量名称

Go 中的变量名称尽可能短为妙,言简意赅,尤其是对于那些处于有限空间中的局部变量更是如此。例如: 用 c 而不是 lineCount; 用 i 而不是 sliceIndex

基本准则:当变量首次被使用时离声明的位置越远,变量名称必须更具描述性。对于方法接收者的名称来说,一个或者两个字母就足够了。普通变量比如 loop indicesreaders 的名称用一个字母(i , r)指代就可以。

当然,对于一些逻辑比较复杂的逻辑,或者非常规事物,或者全局变量则需使用更具描述性的名字。

来说两句吧
最新评论