go编程规范

栏目: Go · 发布时间: 5年前

内容简介:符合除标准库外,Go 语言的导入路径基本上依赖代码托管平台上的 URL 路径,因此一个源文件需要导入的包有 4 种分类:标准库、第三方包、组织内其它包和当前包的子包。基本规则:

导入标准库、第三方或其它包

符合 go fmt 规范

除标准库外,Go 语言的导入路径基本上依赖代码托管平台上的 URL 路径,因此一个源文件需要导入的包有 4 种分类:标准库、第三方包、组织内其它包和当前包的子包。

基本规则:

  • 如果同时存在 2 种及以上,则需要使用分区来导入。每个分类使用一个分区,采用空行作为分区之间的分割。
  • 在非测试文件( *_test.go )中,禁止使用 . 来简化导入包的对象调用。
  • 禁止使用相对路径导入( ./subpackage ),所有导入路径必须符合 go get 标准。

下面是一个完整的示例:

import (
	"flag"

	"github.com/go-openapi/spec"
	"github.com/robfig/cron"

	"xxx"
)

注释规范

符合 github.com/golang/lint/golint 规范

  • 所有导出对象都需要注释说明其用途;非导出对象根据情况进行注释。
  • 如果对象可数且无明确指定数量的情况下,一律使用单数形式和一般进行时描述;否则使用复数形式。
  • 包、函数、方法和类型的注释说明都是一个完整的句子。
  • 句子类型的注释首字母均需大写;短语类型的注释首字母需小写。

包级别

  • 包级别的注释就是对包的介绍,只需在同个包的任一源文件中说明即可有效。

  • 对于 main 包,一般只有一行简短的注释用以说明包的用途,且以项目名称开头:

    // Gogs (Go Git Service) is a painless self-hosted Git Service.
    	package main
  • 对于一个复杂项目的子包,一般情况下不需要包级别注释,除非是代表某个特定功能的模块。

  • 对于简单的非 main 包,也可用一行注释概括。

  • 对于相对功能复杂的非 main 包,一般都会增加一些使用示例或基本说明,且以 Package <name> 开头:

    /*
    	Package regexp implements a simple library for regular expressions.
    
    	The syntax of the regular expressions accepted is:
    
    	    regexp:
    	        concatenation { '|' concatenation }
    	    concatenation:
    	        { closure }
    	    closure:
    	        term [ '*' | '+' | '?' ]
    	    term:
    	        '^'
    	        '$'
    	        '.'
    	        character
    	        '[' [ '^' ] character-ranges ']'
    	        '(' regexp ')'
    	*/
    	package regexp
  • 特别复杂的包说明,可单独创建 doc.go 文件来加以说明。

结构、接口及其它类型

  • 类型的定义一般都以单数形式描述:

    // Request represents a request to run a command.
    	type Request struct { ...
  • 如果为接口,以 er 作为后缀,接口的实现则去掉 er ,则一般以以下形式描述:

    // FileInfo is the interface that describes a file and is returned by Stat and Lstat.
    	type Reader interface { ...

函数与方法

  • 函数与方法的注释需以函数或方法的名称作为开头:

    // Post returns *BeegoHttpRequest with POST method.
  • 如果一句话不足以说明全部问题,则可换行继续进行更加细致的描述:

    // Copy copies file from source to target path.
    	// It returns false and error when error occurs in underlying function calls.
  • 若函数或方法为判断类型(返回值主要为 bool 类型),则以 <name> returns true if 开头:

    // HasPrefix returns true if name has any string in given slice as prefix.
    	func HasPrefix(name string, prefixes []string) bool { ...

其它说明

  • 当某个部分等待完成时,可用 TODO: 开头的注释来提醒维护人员。

  • 当某个部分存在已知问题进行需要修复或改进时,可用 FIXME: 开头的注释来提醒维护人员。

  • 当需要特别说明某个问题时,可用 NOTE: 开头的注释:

    // NOTE: os.Chmod and os.Chtimes don't recognize symbolic link,
    	// which will lead "no such file or directory" error.
    	return os.Symlink(target, dest)

命名规则

文件名

  • 整个应用或包的主入口文件应当是 main.go 或与应用名称简写相同。例如: Gogs 的主入口文件名为 gogs.go 。普通文件命名应当是全部小写 context.go 如复杂文件名应当是使用下划线分词 file_reader.go

函数或方法

  • 若函数或方法为判断类型(返回值主要为 bool 类型),则名称应以 Has , Is 等判断性动词开头:

    func HasPrefix(name string, prefixes []string) bool { ... }
    	func IsEntry(name string, entries []string) bool { ... }

常量

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

    const APP_VER = "0.7.0.1110 Beta"
  • 如果是枚举类型的常量,需要先创建相应类型:

    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
    	)

变量

  • 变量命名基本上遵循相应的英文表达或简写。

  • 在相对简单的环境(对象数量少、针对性强)中,可以将一些名称由完整单词简写为单个字母,例如:

    • user 可以简写为 u
    • userID 可以简写 uid
  • 若变量类型为 bool 类型,则名称应以 Has , Is 开头:

    var isExist bool
    	var hasConflict bool
  • 上条规则也适用于结构定义:

    // Webhook represents a web hook object.
    	type Webhook struct {
    		ID           int64 `gorm:"id"`
    		RepoID       int64
    		OrgID        int64
    		URL          string `gorm:"url TEXT"`
    		ContentType  HookContentType
    		Secret       string `gorm:"TEXT"`
    		Events       string `gorm:"TEXT"`
    		*HookEvent   `gorm:"-"`
    		IsSSL        bool `gorm:"is_ssl"`
    		IsActive     bool
    		HookTaskType HookTaskType
    		Meta         string     `gorm:"TEXT"` // store hook-specific attributes
    		LastStatus   HookStatus // Last delivery status
    		Created      time.Time  `gorm:"CREATED"`
    		Updated      time.Time  `gorm:"UPDATED"`
    	}

变量命名惯例

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

  • 如果变量为私有,且特有名词为首个单词,则使用小写,如 apiClient
  • 其它情况都应当使用该名词原有的写法,如 APIClientrepoIDUserID

下面列举了一些常见的特有名词:

// A GonicMapper that contains a list of common initialisms taken from golang/lint
var LintGonicMapper = GonicMapper{
	"API":   true,
	"ASCII": true,
	"CPU":   true,
	"CSS":   true,
	"DNS":   true,
	"EOF":   true,
	"GUID":  true,
	"HTML":  true,
	"HTTP":  true,
	"HTTPS": true,
	"ID":    true,
	"IP":    true,
	"JSON":  true,
	"LHS":   true,
	"QPS":   true,
	"RAM":   true,
	"RHS":   true,
	"RPC":   true,
	"SLA":   true,
	"SMTP":  true,
	"SSH":   true,
	"TLS":   true,
	"TTL":   true,
	"UI":    true,
	"UID":   true,
	"UUID":  true,
	"URI":   true,
	"URL":   true,
	"UTF8":  true,
	"VM":    true,
	"XML":   true,
	"XSRF":  true,
	"XSS":   true,
}

声明语句

函数或方法

函数或方法的参数排列顺序遵循以下几点原则(从左到右):

  1. 参数的重要程度与逻辑顺序
  2. 简单类型优先于复杂类型
  3. 尽可能将同种类型的参数放在相邻位置,则只需写一次类型

示例

以下声明语句, User 类型要复杂于 string 类型,但由于 RepositoryUser 的附属品,首先确定 User 才能继而确定 Repository 。因此, User 的顺序要优先于 repoName

func IsRepositoryExist(user *User, repoName string) (bool, error) { ...

代码指导

基本约束

  • 所有应用的 main 包需要有 APP_VER 常量表示版本,格式为 X.Y.Z.Date [Status] ,例如: 0.7.6.1112 Beta

  • 单独的库需要有函数 Version 返回库版本号的字符串,格式为 X.Y.Z[.Date]

  • 当单行代码超过 80 个字符时,就要考虑分行。分行的规则是以参数为单位将从较长的参数开始换行,以此类推直到每行长度合适:

    So(z.ExtractTo(
    		path.Join(os.TempDir(), "testdata/test2"),
    		"dir/", "dir/bar", "readonly"), ShouldBeNil)
  • 当单行声明语句超过 80 个字符时,就要考虑分行。分行的规则是将参数按类型分组,紧接着的声明语句的是一个空行,以便和函数体区别:

    // NewNode initializes and returns a new Node representation.
    	func NewNode(
    		importPath, downloadUrl string,
    		tp RevisionType, val string,
    		isGetDeps bool) *Node {
    
    		n := &Node{
    			Pkg: Pkg{
    				ImportPath: importPath,
    				RootPath:   GetRootPath(importPath),
    				Type:       tp,
    				Value:      val,
    			},
    			DownloadURL: downloadUrl,
    			IsGetDeps:   isGetDeps,
    		}
    		n.InstallPath = path.Join(setting.InstallRepoPath, n.RootPath) + n.ValSuffix()
    		return n
    	}
  • 定义对象函数时,指针用p:

    func (p *Node)NewNode(){}
  • 分组声明一般需要按照功能来区分,而不是将所有类型都分在一组:

    const (
    		// Default section name.
    		DEFAULT_SECTION = "DEFAULT"
    		// Maximum allowed depth when recursively substituing variable names.
    		_DEPTH_VALUES = 200
    	)
    
    	type ParseError int
    
    	const (
    		ERR_SECTION_NOT_FOUND ParseError = iota + 1
    		ERR_KEY_NOT_FOUND
    		ERR_BLANK_SECTION_NAME
    		ERR_COULD_NOT_PARSE
    	)

测试用例

  • 单元测试都必须使用 GoConvey 编写,且辅助包覆盖率必须在 80% 以上。

使用示例

  • 为辅助包书写使用示例的时,文件名均命名为 example_test.go
  • 测试用例的函数名称必须以 Test_ 开头,例如: Test_Logger
  • 如果为方法书写测试用例,则需要以 Text_<Struct>_<Method> 的形式命名,例如: Test_Macaron_Run

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

计算机和难解性

计算机和难解性

M.R 加里、D.S. 约翰逊 / 张立昂、沈泓 / 科学出版社 / 1987年 / 4.50

本书系统地介绍了NP完全性理论的概念和方法,全书共分为7章和两个附录。第一章粗略地介绍了计算复杂性的一些基本概念和NP完全性理论的意义。第二章至第五章介绍了NP完全性的基本理论和证明的方法。第六章集中研究NP难问题的近似算法。第七章概述了大量计算复杂性中的有关理论课题。 附录A收集了范围广泛、内容丰富的NP完全性和NP难的问题、附录B补充了NP问题的一些最新的进展,既有理论方面的,又有关于具体问题......一起来看看 《计算机和难解性》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具