【Go - 规范】

在Go语言中,有一些推荐的目录和代码规范,这些规范有助于保持代码的一致性、可读性和可维护性。

目录规范

示例

  1. 项目根目录

    • cmd/:包含应用程序的主包,每个子目录对应一个可执行文件。
    • pkg/:包含可重用的库代码,这些代码可以被其他项目导入。
    • internal/:包含私有的应用程序和库代码,这些代码只能在该模块内使用。
    • api/:包含API定义和协议文件(如Protobuf、GraphQL等)。
    • web/:包含前端代码(如HTML、CSS、JavaScript等)。
    • configs/:包含配置文件模板或默认配置。
    • scripts/:包含构建、安装、分析等脚本。
    • build/:包含打包和持续集成相关的文件和脚本。
    • docs/:包含项目的文档。
    • test/:包含外部测试应用程序和测试数据。
  2. 示例目录结构

    myproject/
    ├── cmd/
    │   └── myapp/
    │       └── main.go
    ├── pkg/
    │   └── mylib/
    │       └── mylib.go
    ├── internal/
    │   └── myinternal/
    │       └── myinternal.go
    ├── api/
    ├── web/
    ├── configs/
    ├── scripts/
    ├── build/
    ├── docs/
    ├── test/
    └── go.mod
    
    

文件命名规范

在 Go 语言中,文件命名遵循简单的约定,主要是为了保持代码的清晰和一致性。以下是一些常见的指导原则:

  1. 小写字母:通常,Go 文件名使用全小写字母。这有助于避免在大小写敏感和不敏感的文件系统之间移植代码时出现的问题。
  2. 下划线分隔:如果文件名由多个单词组成,通常使用下划线(_)来分隔单词,例如 my_example.go
  3. 短划线:虽然不太常见,但有时也会看到使用短划线(-)作为单词分隔符的文件名,如 my-example.go。这种命名方式在某些项目中可能会遇到,但在 Go 社区中,下划线更为普遍。
  4. 特殊后缀:Go 文件可以根据它们的用途或目标操作系统/架构有特殊的后缀。例如,针对 Windows 平台特定的代码可能会命名为 file_windows.go,而针对测试的文件通常以 _test.go 结尾,如 example_test.go
  5. 避免使用 Go 保留关键字:文件名应避免使用 Go 的保留关键字,如 map.gofunc.go
  6. 简洁明了:文件名应该简短且具有描述性,准确反映文件内容的用途或功能。

代码规范

  1. 命名规范
    • 包名:使用小写字母,避免使用下划线或混合大小写。包名应简洁且具有描述性。
    • 变量名:使用驼峰命名法(camelCase),对于导出的变量使用首字母大写。
    • 常量名:使用大写字母和下划线分隔(如MAX_BUFFER_SIZE)。
    • 函数名:使用驼峰命名法(camelCase),对于导出的函数使用首字母大写。
  2. 代码风格
    • 格式化:使用gofmt工具格式化代码。
    • 注释:使用//进行单行注释,使用/* ... */进行多行注释。导出的包、函数、类型和变量应有注释。
    • 错误处理:使用error类型处理错误,尽量避免使用panic
    • 结构体标签:使用反引号(```)定义结构体标签,如JSON标签。
  3. 代码组织
    • 单一职责原则:每个包应有单一的职责,避免包的职责过于复杂。
    • 模块化:将相关功能组织到同一个包中,避免包之间的循环依赖。
    • 测试:为每个包编写单元测试,测试文件以_test.go结尾,并放在与被测试代码相同的包中。

示例代码

package main

import (
    "errors"
    "fmt"
)

// User represents a user in the system
type User struct {
    ID    int
    Name  string
    Email string
}

// NewUser creates a new user
func NewUser(id int, name, email string) (*User, error) {
    if id <= 0 {
        return nil, errors.New("invalid ID")
    }
    if name == "" {
        return nil, errors.New("name cannot be empty")
    }
    if email == "" {
        return nil, errors.New("email cannot be empty")
    }
    return &User{ID: id, Name: name, Email: email}, nil
}

func main() {
    user, err := NewUser(1, "John Doe", "john@example.com")
    if err != nil {
        fmt.Println("Error:", err)
        return
    }
    fmt.Printf("User: %+v\\n", user)
}

接口规范

  1. 接口命名
    • 接口名通常以er结尾,例如ReaderWriterCloser等。
    • 接口名应简洁且具有描述性。
  2. 接口设计
    • 小接口:尽量设计小而专一的接口,避免大而复杂的接口。例如,标准库中的io.Readerio.Writer接口。
    • 组合接口:通过组合小接口来构建更复杂的接口。例如,io.ReadWriter接口组合了io.Readerio.Writer接口。
    • 接口类型:接口类型通常用于定义行为,而不是数据结构。
  3. 接口实现
    • 隐式实现:Go语言的接口实现是隐式的,即不需要显式声明某个类型实现了某个接口,只要该类型实现了接口中的所有方法即可。
    • 零值接口:接口的零值是nil,在使用接口时要注意检查接口是否为nil

示例代码

package main

import (
    "fmt"
    "io"
)

// Reader 是一个读取数据的接口
type Reader interface {
    Read(p []byte) (n int, err error)
}

// Writer 是一个写入数据的接口
type Writer interface {
    Write(p []byte) (n int, err error)
}

// ReadWriter 是一个组合接口,包含 Reader 和 Writer
type ReadWriter interface {
    Reader
    Writer
}

// MyReadWriter 是一个实现了 ReadWriter 接口的类型
type MyReadWriter struct{}

func (rw *MyReadWriter) Read(p []byte) (n int, err error) {
    copy(p, "hello")
    return 5, nil
}

func (rw *MyReadWriter) Write(p []byte) (n int, err error) {
    fmt.Println(string(p))
    return len(p), nil
}

func main() {
    var rw ReadWriter = &MyReadWriter{}
    buf := make([]byte, 10)
    rw.Read(buf)
    rw.Write(buf)
}

日志规范

  1. 日志库
    • 使用标准库log包或第三方日志库(如logruszap)来记录日志。
    • 第三方日志库通常提供更丰富的功能和更好的性能。
  2. 日志级别
    • 使用不同的日志级别来记录不同严重程度的日志信息。常见的日志级别包括:DEBUGINFOWARNERRORFATAL
    • 根据日志级别的不同,决定是否输出日志信息。例如,在生产环境中可能只记录WARN及以上级别的日志。
  3. 日志格式
    • 统一日志格式,包含时间戳、日志级别、日志消息等信息。
    • 使用结构化日志记录,方便日志的解析和查询。
  4. 日志输出
    • 日志可以输出到控制台、文件或远程日志服务器。
    • 使用日志轮转(log rotation)来管理日志文件的大小和数量。

示例代码

使用标准库log包:

package main

import (
    "log"
    "os"
)

func main() {
    // 创建一个日志文件
    file, err := os.OpenFile("app.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
    if err != nil {
        log.Fatal(err)
    }
    defer file.Close()

    // 设置日志输出到文件
    log.SetOutput(file)

    // 设置日志前缀和标志
    log.SetPrefix("INFO: ")
    log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)

    // 记录日志
    log.Println("This is an info message")
    log.Println("This is another info message")
}

使用第三方日志库logrus

package main

import (
    "github.com/sirupsen/logrus"
    "os"
)

func main() {
    // 创建一个新的日志实例
    logger := logrus.New()

    // 设置日志输出到文件
    file, err := os.OpenFile("app.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
    if err != nil {
        logger.Fatal(err)
    }
    defer file.Close()
    logger.SetOutput(file)

    // 设置日志格式为JSON格式
    logger.SetFormatter(&logrus.JSONFormatter{})

    // 设置日志级别
    logger.SetLevel(logrus.InfoLevel)

    // 记录日志
    logger.Info("This is an info message")
    logger.Warn("This is a warning message")
    logger.Error("This is an error message")
}

结语

通过遵循这些规范,希望可以帮助你编写出更清晰、可维护的Go代码。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,335评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,895评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,766评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,918评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,042评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,169评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,219评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,976评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,393评论 1 304
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,711评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,876评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,562评论 4 336
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,193评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,903评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,142评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,699评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,764评论 2 351

推荐阅读更多精彩内容

  • 写完代码都必须格式化,保证代码优雅:gofmt goimports 编译前先执行代码静态分析:go vet pat...
    Gundy_阅读 425评论 0 4
  • 目录 统一规范篇 命名篇 开发篇 优化篇 统一规范篇 本篇主要描述了公司内部同事都必须遵守的一些开发规矩,如统一开...
    零一间阅读 1,916评论 0 2
  • 项目目录结构规范 TODO 命名规范 目录名 目录名全部小写,没有大写或下划线。 文件 文件名要简短有意义,应小写...
    wayyyy阅读 698评论 0 0
  • Uber Go 语言编码规范 Uber 是一家美国硅谷的科技公司,也是 Go 语言的早期 adopter。其开源了...
    知识铺阅读 522评论 0 0
  • 内容列表 指导原则指向interface的指针接收器(receiver)与接口零值Mutex是有效的在边界处拷贝S...
    金科_阅读 1,032评论 0 0