Maison  >  Article  >  développement back-end  >  Partagez un ensemble de normes de codage Go ! Bienvenue à collectionner !

Partagez un ensemble de normes de codage Go ! Bienvenue à collectionner !

藏色散人
藏色散人avant
2022-12-09 17:08:425599parcourir

Récemment, j'ai révisé beaucoup de code du langage Go dans le projet. Il est nécessaire de résumer les spécifications du code, qui peuvent être considérées comme un enregistrement de notes.

Comme mentionné précédemment, il ne s'agit que d'un ensemble de normes pour notre équipe.

Aujourd'hui, nous parlons des normes de codage de Go, qui sont grossièrement divisées en plusieurs modules principaux, tels que les notes sur le package/la dénomination des variables/constantes, la syntaxe de base, les fonctions, la gestion des erreurs, l'expérience, etc. [Recommandé : Tutoriel Golang]

1. Style de code

1.1 Format du code

  • Le code doit être formaté avec Gofmt peut être configuré par vous-même
  • Le code que nous écrivons doit. ne dépasse pas 1 ligne par ligne de 120 caractères, tout excès sera résolu par des sauts de ligne.
  • Le nombre maximum de lignes dans un seul fichier ne dépasse pas 800 lignes.
  • Le nombre maximum de lignes dans une seule fonction ne dépasse pas 80 lignes.
  • Spécification d'importation
    • N'utilisez pas de chemins relatifs pour introduire des packages, par exempleimport ../util/net
    • Lors de l'importation d'un package, lorsque plusieurs noms de packages identiques sont en conflit, vous devez utiliser des alias d'importation
// bad
"github.com/google/uuid"

// good
uuid "github.com/google/uuid"
  • Il est recommandé que les packages importés soient paquets anonymes regroupés et référencés Il est recommandé d'utiliser un nouveau groupe et d'ajouter des commentaires pour faciliter la lecture par des amis plus tard
import (
    // Go 标准库
    "fmt"

    //第三方包
    "github.com/jinzhu/gorm"
    "github.com/google/uuid"
    "github.com/go-redis/redis/v8"

    // 匿名包
    /import mysql driver
    _"github.com/jinzhu/gorm/dialects/mysql"

    // 内部包
    slice "xxx.local/pkg/v1/goslice"
    meta "xxx.local/pkg/v1/meta"
    gomap "xxx.local/pkg/v2/gomap")

1.2 Déclaration, initialisation et définition

  • Lorsqu'une fonction doit utiliser plusieurs variables, vous pouvez utiliser la déclaration var au début de la fonction. Les variables déclarées en dehors de la fonction ne peuvent pas utiliser :=, ce qui peut entraîner des pièges. Si vous ne le savez pas, vous pouvez laisser un message dans la zone de commentaire (ce n'est pas facile de commenter) !
var (
    port = 8081
    metricServerPort = 2001)
  • Utilisez &struct au lieu de new(struct) lors de l'initialisation de la structure pour vous assurer qu'elle est cohérente avec l'initialisation de la structure, et enveloppez la ligne lors de l'initialisation de la structure.
// bad
stu := new(S)
stu.Name = "张三"

// good
stu := &S{
    Name:"李四"
}
  • Utilisez make pour spécifier la capacité du conteneur lors de la déclaration d'une carte, d'un tableau, etc., afin de pré-allouer le contenu.
users := make(map[int]string, 10)tags := make([]int, 0, 10)
  • Utilisez le mot-clé standard var et ne spécifiez pas de type à moins qu'il ne soit différent du type de l'expression.
// bad
var _f string F()

func F() string {
    return "hello world!"
}

// good 
var _f F()

func F() string {
    return "hello world!"
}

1.3 Traitement des erreurs

  • Si la fonction renvoie une erreur, l'erreur doit être traitée. Si l'entreprise le permet, vous pouvez utiliser _ pour l'accepter et l'ignorer. Le report correspondant peut être traité sans traitement explicite.
// bad
func InitConfig() error {
    ...
}
InitConfig()


// good
func InitConfig() error {
    ...
}
err := InitConfig()
if err != nil {
    ...
}
// or 
_ := InitConfig()
  • error doit être renvoyé comme dernier paramètre lorsqu'il est utilisé comme valeur de retour
// bad
func InitConfig() (error,int) {
    ...
}

// good 
func InitConfig() (int, error) {
    ...
}
  • Les erreurs doivent être traitées séparément et essayer de ne pas être couplées à une autre logique.
// bad
res, err := InitConfig()
if err != nil || res != nil {
    return err
}

// good
res, err := InitConfig()
if err != nil {
    return err
}
if res != nil {
    return fmt.Errorf("invalid result")
}

1.4 Gestion de la panique

  • Il est interdit de lancer des erreurs de panique dans le code métier.
  • La panique ne peut apparaître qu'avant le démarrage du service, comme la lecture de la configuration, le stockage des liens (redis, mysql, etc.).
  • Il est recommandé d'utiliser error au lieu de panic dans le code commercial.

1.5 Test unitaire

  • Un cas de test doit être écrit pour chaque fonction importante, et tous les tests doivent être automatiquement exécutés lors de la fusion du code.
  • Le fichier s'appelle xxx_test.go.
  • Il est recommandé d'utiliser le nom de la fonction Test lors de la dénomination des fonctions.

2. Conventions de dénomination

Dans chaque langue, les conventions de dénomination sont très importantes dans les spécifications du code. Une dénomination unifiée et précise peut non seulement améliorer la lisibilité du code, mais aussi donner le sentiment aux gens que ce camarade sait vraiment comment faire. pour le faire. bœuf!

2.1 包命名规范

  • 包名必须与目录名一致(这和其他 php、Java 还是有一点不太一样的),尽量采取有意义、简短的包名,不要与 go 的标准库名称一样。
  • 包名小写,没有下划线,可以使用中划线隔开,使用多级目录来划分目录。
  • 包名不要出现复数命名。
  • 包名命名尽量简单一目了然,ge:user、log。

2.2 文件命名规范

  • 文件名要见名思义,尽量简而短
  • 文件名小写,组合词用下划线分割

2.3 函数命名规范

  • 与 php、Java 一样,必须遵循驼峰规范,Go 语言中需要根据访问的控制决定大驼峰还是小驼峰。
  • 单元测试的函数用大驼峰,TestFunc。

2.4 结构体命名规范

  • 与 php、Java 一样,必须遵循驼峰规范,Go 语言中需要根据访问的控制决定大驼峰还是小驼峰。
  • 避免使用 info 、data 这种无意义的名称。
  • 命名使用名词而非动词。
  • 结构体在声明和初始化的时候需要换行,eg:
type Student struct{
    Name string
    Age uint8}student := Student{
    Name: "张三",
    Age: 18,}

2.5 变量命名规范

  • 和 php、Java 一样,必须遵循驼峰规范,Go 语言中需要根据访问的控制决定大驼峰还是小驼峰。
  • 若变量为私有时,可以使用小写命名。
  • 局部变量可以简写,eg:i 表示 index。
  • 若变量代表 bool 值,则可以使用 Is 、Can、Has 前缀命名,eg:
var isExit boolvar canReturn bool

2.6 常量命名规范

  • 必须遵循驼峰规范,Go 语言中需要根据访问的控制决定大驼峰还是小驼峰。
  • 若代表枚举值,需要先创建。
type Code intconst (
    ErrNotFound Code = iota
    ErrFatal)

3. 类型

3.1 字符串

好像学过的语言中,都是从字符串开始说起的。就像写代码第一行都是从 Hello World!一样!同意的点赞哈。

  • 字符串判空值
// bad
if s == "" {
    ...}
 // good
 if len(s) == 0 {
    ...}
  • 字符串去除前后子串。
// bad
var s1 "hello world"var s2 "hello"var s3 strings.TrimPrefix(s1, s2)
// good
var s1 "hello world"var s2 "hello"var s3 stringif strings.HasPrefix(s1, s2){
    s3 = s1[len(s2):]}

3.2 切片 slice

  • 声明 slice。
// bad
s := []string{}s := make([]string, 10)
// good
var s []string
s := make([]string, 0, 10)
  • 非空判断。
//bad
if len(slice) >0 {
    ...}
 // good
 if slice != nil && len(slice) > 0 {
    ...}
  • slice copy。
// badvar b1,b2 []bytefor i, v := range b1 {
    b2[i] = v}for i := range b1 {
    b2[i] = b1[i]}// goodcopy(b2,b1)
  • slice 新增。
// bad
var a,b []intfor _, v := range a {
    b = append(b,v)}
// good
var a, b []int
b := append(b, a...)

3.4 结构体 struct

  • 初始化需要多行。
type Student struct{
    Name string
    Age uint8}student := Student{
    Name: "张三",
    Age: 18,}

4. 控制语句

4.1 if

  • if 可以用局部变量的方式初始化。
if err := InitConfig; err != nil {
    return err}

4.2 for

  • 不允许在 for 中使用 defer, defer 只在函数结束时才会执行。
// bad
for file := range files {
    fd, err := os.Open(file)
    if err != nil {
        return err    }
    defer fd.close()}
// good
    for file := range files{
    func() {
        fd,err := os.open(file)
        if err!=nil {
            return err        }
        defer fd.close()
    }()}

4.3 range

  • 如果不需要 key 直接用 _ 忽略,value 也一样。
for _, v := range students {
    ...}for i, _ := range students {
    ...}for i, v := range students {
    ...}

注: 若操作指针时请注意不能直接用 s := v。想知道可以评论区告诉我哦!

4.4 switch

  • 和其他语言不一样,必须要有 defalt
switch type {
    case 1:
        fmt.Println("type = 1")
        break
     case 2:
        fmt.Println("type = 2")
        break
     default :
        fmt.Println("unKnown type")}

4.5 goto

  • 业务中不允许使用 goto。
  • 框架和公共工具也不允许使用 goto。

5. 函数

  • 传参和返回的变量小写字母。
  • 传入参数时slice、map、interface、chan 禁止传递指针类型。
  • 采用值传递,不用指针传值。
  • 入参个数不能超出 5 个,超过的可以用 struct 传值。

5.1 函数参数

  • 返回值超出 1 个时,需要用变量名返回。
  • 多个返回值可以用 struct 传。

5.2 defer

  • 当操作资源、或者事物需要提交回滚时,可以在创建开始下方就使用 defer 释放资源。
  • 创建资源后判断 error,非 error 情况后在用 defer 释放。

5.3 代码嵌套

  • 为了代码可读性,为了世界和平,尽量别用太多的嵌套,因为真的很难有人类能看懂。

6. 日常使用感悟

  • 能不用全局变量就不用,可以用参数传值的方式,这样可以大大降低耦合,更有利于单元测试。
  • 衣服开发中,在函数间多用 context 传递上下文,在请求开始时可以生成一个 request_id,便于链路、日志追踪。

6.1 提高性能

  • 在业务开发中,尽量使用 strconv 来替代 fmt。
  • 我们在使用 string 字符串类型时,当修改的场景较多,尽量在使用时用 []byte 来替代。因为每次对 string 的修改都需要重新在申请内存。

6.2 避免踩坑

  • append 要小心自动扩容的情况,最好在申明时分配好容量,避免扩容所带来的性能上的损耗以及分配新的内存地址。若不能确定容量,应选择一个比较大一点的值。
  • 并发场景下,map 非线程安全,需要加锁。还有一种评论区告诉我吧。
  • interface 在编译期间无法被检查,使用上会出现 panic,需要注意

7. 总结

本篇很讲了 Go 语言的编码规范,当时想说的,规范是大家预定的东西,每个公司、团队都会有不一样的规范,只要大家一起遵循就好啦。你可以根据自己团队的需求,定一套属于自己团队的项目规范。如果想小伙伴一起遵循,可以借助一些工具来保障执行度。

讲了很多,虽然很基础,希望对于刚刚转 Go 语言,或者刚学习 Go 语言的同学有帮助吧。今天就到这里了。希望得到大家的一键三连。感谢!

本文系转载,原文链接:mp.weixin.qq.com/s/lfjP9DEia2WL4Ua...

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer