goland代码规范之代码风格

衡量代码质量的唯一标准是阅读该代码时说脏话的次数

一个优秀的团队应当保持一致的代码风格,我们在实践中发现,如果一个项目的代码风格不一致,会导致项目杂乱、不利于跨模块开发、影响开发效率、想骂人等弊端,

在定义我们的编码风格之前,先推荐一本非常好的书籍《clean code》,有时间的可以去看看,磨刀不误砍柴工。

命名基础规则

  • 站在调用者的角度,包不是给你自己用的
  • 简洁、且见名知义
  • 采用通用、大众熟知的缩写命名。比如buf而不是bufio
  • 如果缩写的名字会产生歧义,则放弃或换个

包名

  • 包名与目录名一致
  • 大多数使用命名导入的情况下,不需要重命名

​ 少用调用者去起别名,除非名字太烂

  • 包名应当全部小写、没有下划线、没有大写字母
  • 单数,不用复数

样例:

Bad:

configs
third_party
thirdParty
...

导入包

(1)如果程序包名称与导入路径的最后一个元素不匹配,则必须使用导入别名

import (
    client "example.com/client-go"
    trace "example.com/trace/v2"
)

建议路径最后一个元素是版本号的都使用别名,避免歧义。

(2)在所有其他情况下,除非导入之间有直接冲突,否则应避免导入别名

import (
    "net/http/pprof"
    gpprof "github.com/google/pprof"
)

(3)如有重名,请保留标准包命名,别名自定义或者第三方包

(4)禁止使用相对路径导入,所有导入路径必须符合 go get 标准

文件名

  • 文件名应当保持简单易懂,见名知意
  • 文件名应当全小写,不应出现大写字母,不同单词之间使用下划线分割
  • 文件名应当避免使用复数形式

样例:

Bad:

configs.yaml
cloudServer.go
cloud-server.go

函数名/方法名

  • 函数名/方法名应当保持动词/动宾形式
  • 除非是公认的缩写,否则不应使用缩写
  • 避免命名过长,长命名并不会使其更具可读性,一份有用的说明文档或者注释通常比额外的长名更有价值

样例:

Bad:

container.createContainer(in)

Good:

container.create(in)
  • Go 并不对获取器(getter)和设置器(setter)提供自动支持。针对某个变量或字段,获取器名字无需携带 Get,设置器名字以 Set 开头

note : 若你有个名为 owner (小写,未导出)的字段,其获取器应当名为 Owner(大写,可导出)而非 GetOwner。

样例:

Good:

func Owner() string {
    
    
    ...
}


func SetOwner(ower string) string {
    
    
    ...
}

Bad:

func GetOwner() string {
    
    
    ...
}
  • 命名使用英文,避免单词出错

样例:

Bad

func (u *userRepo) syncDepartmentFormDatabase() {
    
    
    ...
}

From而非Form。

接口名

  • 按照约定,只包含一个方法的接口应当以该方法的名称加上 -er 后缀来命名,如 Reader、Writer、Formatter/CloseNotifier 等
type Reader interface {
    
    
	  Read(p []byte) (n int, err error)
}
  • 名词用于接口名,动词用于接口的方法名
  • 除非包含的方法名与接口名无直接关系,否则都应当使用单个动词
type ContainerRepo interface {
    
    
    Create() error
    Delete() error
    ...
}

结构体名

  • 结构体名严格使用名词形式命名
  • 避免使用复数
  • 多个单词的使用驼峰命名法

样例:

Good:

type Container struct {
    
    }


type ContainerInfo struct {
    
    }

Bad:

type ContainerDelete struct {
    
    }


type Containers struct {
    
    }


type Containerinfo struct {
    
    }

常量名

  • 如果是枚举类型的常量,需要先创建相应类型
type Scheme string


const (
    Http  Scheme = "http"
    Https Scheme = "https"
)
  • 如果模块的功能较为复杂、常量名称容易混淆的情况下,为了更好地区分枚举类型,可以使用完整的前缀
type Symbol string


const (
    SymbolAdd Symbol = "+"
    SymbolSub Symbol = "-"
)

变量名

  • 如果是全局变量,务必使用全称并务必加上注释
// configPath is the absolute path of config file from the command line.
var configPath string
  • 如果是局部变量,在相对简单的环境(对象数量少、针对性强)中,可以将一些名称由完整单词简写为单个字母
user 可以简写为 u
userId 可以简写 uid
  • 若变量类型为 bool 类型,则名称应以 Has、Is、Can 或 Allow 开头
var isExist bool
var hasConflict bool
var canManage bool
var allowGitHook bool

Error

  • Error类型的命名以Error结尾
type CodeError struct {
    
    
    message string
}
  • Error类型的变量,以Err开头
var ErrTokenExpired = errors.New("token expired")
  • 返回类型为Error的变量缩写采用err
func test() {
    
    
    res, err := container.create(in)
    ...
}
  • 不同类型的error不要使用同一个err接收

样例:

Good:

func (c *container)Update(ctx context.Context,name string) error {
    
    
    res, listErr := c.list()
    if listErr!=nil {
    
    
        return listErr    
    }


    updateErr := c.update(name)
    if updateErr!=nil {
    
    
        return updateErr    
    }
    ...
}

Bad:

func (c *container)Update(ctx context.Context,name string) error {
    
    
    res, err := c.list()
    if err!=nil {
    
    
        return err    
    }


    _, err = c.update(name)
    if err!=nil {
    
    
        return err    
    }
    ...
}

URL

  • URL 命名全部小写
  • 用正斜杠/表明层级关系
  • 使用连字符-来提高长路径中名称的可读性,不得在 URL 中使用下划线_
  • URL 结尾不应包含正斜杠/
  • 文件扩展名不应包含在 URL 中
  • URL 需见名知意,但不可暴露服务器架构

Bad:

/GetUserInfo
/photos_path
/My-Folder/my-doc/
/user/user-list

Good:

/user/list
/user/operator-logs

Proto

  • service的rpc应参考函数名/方法名的风格
  • 在proto文件中,结构体(proto的message)命名风格如下:
message CreateSecurityGroupRequest {}


message CreateSecurityResponse {}


// 除了request和response外,其他的命名风格需保持名词形式
message SecurityGroupInfo{}


enum Policy{}

其他

  • 包内容的名字不可以包名开头,因为无需重复包名

举个例子:标准包http 包提供的 HTTP 服务名为 http.Server ,而非 HTTPServer 。用户代码通过 http.Server 引用该类型,因此没有歧义。

package http


type Server struct {
    
    }
  • 不同包中的类型名可以相同,因为客户端可通过包名区分它们

举个例子:标准库中含有多个名为 Reader 的类型,包括 jpeg.Readerbufio.Readercsv.Reader。每个包名搭配 Reader 都是个不错的类型名。

package jpeg


type Reader struct {
    
    }
package bufio


type Reader struct {
    
    }

格式化

我们没有太多可选的余地,因为 Go 已经规范好了,在 Go 世界没有此类战争。

Json/Bson

  • 需要特别指出的是,我们在定义结构体时经常需要id这个字段,为了简明易懂无歧义,我们规定,在一个结构体中,凡是涉及到id字段的,都必须要加上前缀,用以区分
  • 所有json/bson字段多个单词的都使用下划线格式,不使用驼峰格式
type SecurityGroup struct {
    
    
    SgID string `json:"sg_id"`
    Rule Rule `json:"rule"`
    ...
}
type Rule struct {
    
    
    RuleID string `json:"rule_id"`
    ...
}

空行风格

  • 一段代码逻辑处理完后,应当空一行
  • if err后应当空一行

样例:

Good:

func Create() error {
    
    
    if err:=container.create();err!=nil {
    
    
        return err    
    }


    if err:=store.update();err!=nil {
    
    
        return err    
    }
    ...
}
func check() error {
    
    
    isExist, err := contaier.check()
    if err!=nil {
    
    
        return err
    }
    if isExist {
    
    
        ...
    }


    if err:=store.update();err!=nil {
    
    
        return err    
    }
    ...
}
  • 函数内第一行代码不空行

样例:

Bad:

func test() error {
    
    
    
    if err:=container.create();err!=nil {
    
    
        return err
    }
    ...
}
  • 建议函数内最后一行的return语句前一行是空行
func test() error {
    
    
    fmt.Println("test")


    return nil
}

import风格

  • 遵循我们自定义的import顺序:标准包、第三方包、该项目包,三者之间用空行隔开
import (
	"flag"
	"os"

	"github.com/go-kratos/kratos/v2"
	"github.com/go-kratos/kratos/v2/config"
	"github.com/go-kratos/kratos/v2/config/file"
	"github.com/go-kratos/kratos/v2/log"
	"github.com/go-kratos/kratos/v2/middleware/tracing"
	"github.com/go-kratos/kratos/v2/transport/grpc"
	"github.com/go-kratos/kratos/v2/transport/http"

	"backend/internal/conf"
	"backend/internal/data"
)

猜你喜欢

转载自blog.csdn.net/qq_26356861/article/details/131289555