golang编程规范

文章目录

      • 1:使用适当的缩进
      • 2:规范地导入包
      • 3:使用描述性变量和函数名称
      • 4:限制行长
      • 5:使用常量来代替魔术值
      • 6:错误处理
      • 7:避免使用全局变量
      • 8:使用结构体处理复杂数据
      • 9:给代码加注释
      • 10:使用 goroutine 实现并发
      • 11: 使用 Recover 处理 panic
      • 12:避免使用 init 函数
      • 13:使用延迟执行释放资源
      • 14: 复合文本比构造器函数更好
      • 15: 最小化函数参数
      • 16:使用显式返回值,而非命名返回值
      • 17: 尽可能将函数的复杂度降到最低
      • 18:避免变量影子化
      • 19: 使用接口进行抽象
      • 20:避免将库包和可执行文件混在一起

1:使用适当的缩进

适当的缩进让您的代码可读。一致使用制表符或者空格(最好使用制表符),按照 Go 的标准约定进行缩进。

package main
import "fmt"func main() {for i := 0; i < 5; i++ {fmt.Println("Hello, World!")}
}

运行 gofmt 以根据 Go 规范自动格式化(缩进)代码。

$ gofmt -w your_file.go

2:规范地导入包

仅导入所有必需的包,并且按照分组标准库包、第三方包和私有包的格式整理导入部分。

package main
import ("fmt""math/rand""time"
)

3:使用描述性变量和函数名称

有意义的名称:使用传达变量用途的名称。
驼峰式大小写:以小写字母开头,并大写名称中每个后续单词的第一个字母。
短名称:对于作用域小的短生命周期变量,可以使用简短、简洁的名称。
避免缩写:避免使用神秘的缩写和首字母缩写,而采用描述性名称。
一致性:在整个代码库中保持命名一致性。

package main
import "fmt"func main() {// Declare variables with meaningful namesuserName := "John Doe"   // CamelCase: Start with lowercase and capitalize subsequent words.itemCount := 10         // Short Names: Short and concise for small-scoped variables.isReady := true         // No Abbreviations: Avoid cryptic abbreviations or acronyms.// Display variable valuesfmt.Println("User Name:", userName)fmt.Println("Item Count:", itemCount)fmt.Println("Is Ready:", isReady)
}// Use mixedCase for package-level variables
var exportedVariable int = 42// Function names should be descriptive
func calculateSumOfNumbers(a, b int) int {return a + b
}// Consistency: Maintain naming consistency throughout your codebase.

4:限制行长

当可能时,将代码行保持在 80 个字符以内,以提高可读性。

package main
import ("fmt""math"
)func main() {result := calculateHypotenuse(3, 4)fmt.Println("Hypotenuse:", result)
}func calculateHypotenuse(a, b float64) float64 {return math.Sqrt(a*a + b*b)
}

5:使用常量来代替魔术值

在代码中避免使用魔术值。魔术值是散布在代码中各处的硬编码的数字或字符串,缺乏上下文,让人难以理解其目的。 为其定义常量,以提高代码的可维护性。

package main
import "fmt"const (// Define a constant for a maximum number of retriesMaxRetries = 3// Define a constant for a default timeout in secondsDefaultTimeout = 30
)func main() {retries := 0timeout := DefaultTimeoutfor retries < MaxRetries {fmt.Printf("Attempting operation (Retry %d) with timeout: %d seconds\n", retries+1, timeout)// ... Your code logic here ...retries++}
}

6:错误处理

Go 鼓励开发者明确处理错误,原因如下:

安全性:错误处理可确保意外问题不会导致程序突然惊慌失措或崩溃。
清晰性:明确的错误处理使得代码具有更好的可读性,并有助于识别可能的错误位置。
调试:处理错误可为调试和故障排除提供有价值的信息。

package main
import ("fmt""os"
)func main() {// Open a filefile, err := os.Open("example.txt")if err != nil {// Handle the errorfmt.Println("Error opening the file:", err)return}defer file.Close() // Close the file when done// Read from the filebuffer := make([]byte, 1024)_, err = file.Read(buffer)if err != nil {// Handle the errorfmt.Println("Error reading the file:", err)return}// Print the file contentfmt.Println("File content:", string(buffer))
}

7:避免使用全局变量

尽量减少使用全局变量。全局变量会导致行为不可预测、调试困难以及阻碍代码复用。全局变量也可能在程序的不同部分之间引入不必要的依赖。相反,应通过函数参数和返回值传递数据。

package main
import ("fmt"
)func main() {// Declare and initialize a variable within the main functionmessage := "Hello, Go!"// Call a function that uses the local variableprintMessage(message)
}// printMessage is a function that takes a parameter
func printMessage(msg string) {fmt.Println(msg)
}

8:使用结构体处理复杂数据

使用结构体将相关数据字段和方法组合在一起。它们允许你将相关变量组合在一起,让你的代码更井井有条且更易读。

package main
import ("fmt"
)// Define a struct named Person to represent a person's information.
type Person struct {FirstName string // First name of the personLastName  string // Last name of the personAge       int    // Age of the person
}func main() {// Create an instance of the Person struct and initialize its fields.person := Person{FirstName: "John",LastName:  "Doe",Age:       30,}// Access and print the values of the struct's fields.fmt.Println("First Name:", person.FirstName) // Print first namefmt.Println("Last Name:", person.LastName)   // Print last namefmt.Println("Age:", person.Age)             // Print age
}

9:给代码加注释

添加注释以解释你的代码的功能,尤其是复杂或不明显的代码段。

  • 单行注释
    单行注释以 // 开始。使用它们来解释特定的代码行。
package main
import "fmt"func main() {// This is a single-line commentfmt.Println("Hello, World!") // Print a greeting
}
  • 多行注释
    多行注释被包裹在 /* */ 中。使用多行注释可以在多行上提供更长的解释或注释。
package mainimport "fmt"func main() {/*This is a multi-line comment.It can span several lines.*/fmt.Println("Hello, World!") // Print a greeting
}
  • 函数说明
    添加说明函数的功能、参数和返回值的注释。 使用 godoc 样式为函数注释。
package mainimport "fmt"// greetUser greets a user by name.
// Parameters:
//   name (string): The name of the user to greet.
// Returns:
//   string: The greeting message.
func greetUser(name string) string {return "Hello, " + name + "!"
}func main() {userName := "Alice"greeting := greetUser(userName)fmt.Println(greeting)
}
  • 程序包注释
    在 Go 文件的顶部添加注释,以描述程序包的用途。使用相同 的 godoc 样式。
package main
import "fmt"// greetUser greets a user by name.
// Parameters:
//   name (string): The name of the user to greet.
// Returns:
//   string: The greeting message.
func greetUser(name string) string {return "Hello, " + name + "!"
}func main() {userName := "Alice"greeting := greetUser(userName)fmt.Println(greeting)
}

10:使用 goroutine 实现并发

利用 goroutine 高效执行并发操作。goroutine 是 Go 中的轻量级、并发的执行线程。它们使你能够无需传统线程的开销而并发运行函数。这允许你编写高度并发且高效的程序。

package main
import ("fmt""time"
)// Function that runs concurrently
func printNumbers() {for i := 1; i <= 5; i++ {fmt.Printf("%d ", i)time.Sleep(100 * time.Millisecond)}
}// Function that runs in the main goroutine
func main() {// Start the goroutinego printNumbers()// Continue executing mainfor i := 0; i < 2; i++ {fmt.Println("Hello")time.Sleep(200 * time.Millisecond)}// Ensure the goroutine completes 

11: 使用 Recover 处理 panic

使用 recover 函数优雅地处理恐慌并防止程序崩溃。在 Go 中,恐慌是意外的运行时错误,这些错误可能会导致程序崩溃。然而,Go 提供了一种称为 recover 的机制来优雅地处理panic。

package mainimport "fmt"// Function that might panic
func riskyOperation() {defer func() {if r := recover(); r != nil {// Recover from the panic and handle it gracefullyfmt.Println("Recovered from panic:", r)}}()// Simulate a panic conditionpanic("Oops! Something went wrong.")
}func main() {fmt.Println("Start of the program.")// Call the risky operation within a function that recovers from panicsriskyOperation()fmt.Println("End of the program.")
}

12:避免使用 init 函数

除非必要,否则避免使用 init 函数,因为它们会让代码更难理解和维护。
更好的方法是将你的初始化逻辑移动到通常从主函数调用的常规函数中。这会让你获得更好的控制,增强代码可读性并简化测试。

package mainimport ("fmt"
)// InitializeConfig initializes configuration.
func InitializeConfig() {// Initialize configuration parameters here.fmt.Println("Initializing configuration...")
}// InitializeDatabase initializes the database connection.
func InitializeDatabase() {// Initialize database connection here.fmt.Println("Initializing database...")
}func main() {// Call initialization functions explicitly.InitializeConfig()InitializeDatabase()// Your main program logic goes here.fmt.Println("Main program logic...")
}

13:使用延迟执行释放资源

延迟执行允许你延迟执行一个函数,直到周边函数返回。它通常用于关闭文件、解锁互斥锁或释放其他资源的任务

package mainimport ("fmt""os"
)func main() {// Open the file (Replace "example.txt" with your file's name)file, err := os.Open("example.txt")if err != nil {fmt.Println("Error opening the file:", err)return // Exit the program on error}defer file.Close() // Ensure the file is closed when the function exits// Read and print the contents of the filedata := make([]byte, 100)n, err := file.Read(data)if err != nil {fmt.Println("Error reading the file:", err)return // Exit the program on error}fmt.Printf("Read %d bytes: %s\n", n, data[:n])
}

14: 复合文本比构造器函数更好

使用复合文本创建结构实例,而不是构造器函数。
为什么使用复合文本?
复合文本具有以下优点:

  • 简明
  • 可读性
  • 灵活性
package mainimport ("fmt"
)// Define a struct type representing a person
type Person struct {FirstName string // First name of the personLastName  string // Last name of the personAge       int    // Age of the person
}func main() {// Using a composite literal to create a Person instanceperson := Person{FirstName: "John",   // Initialize the FirstName fieldLastName:  "Doe",    // Initialize the LastName fieldAge:       30,       // Initialize the Age field}// Printing the person's informationfmt.Println("Person Details:")fmt.Println("First Name:", person.FirstName) // Access and print the First Name fieldfmt.Println("Last Name:", person.LastName)   // Access and print the Last Name fieldfmt.Println("Age:", person.Age)             // Access and print the Age field
}

15: 最小化函数参数

在 Go 中,编写简洁高效的代码很重要。实现此目标的一种方法是尽量减少函数参数的数量,这样代码的可维护性和可读性会更高。

package mainimport "fmt"// Option struct to hold configuration options
type Option struct {Port    intTimeout int
}// ServerConfig is a function that accepts an Option struct
func ServerConfig(opt Option) {fmt.Printf("Server configuration - Port: %d, Timeout: %d seconds\n", opt.Port, opt.Timeout)
}func main() {// Creating an Option struct with default valuesdefaultConfig := Option{Port:    8080,Timeout: 30,}// Configuring the server with default optionsServerConfig(defaultConfig)// Modifying the Port using a new Option structcustomConfig := Option{Port: 9090,}// Configuring the server with custom Port value and default TimeoutServerConfig(customConfig)
}

在本示例中,我们定义了 Option struct 来保存服务器的配置参数。与向 ServerConfig 函数传递多个参数不同,我们使用一个 Option struct,这使得代码更易于维护和扩展。此方法在使用具有多个配置参数的函数时特别有用。

16:使用显式返回值,而非命名返回值

命名返回值在 Go 中很常见,但有时它们会使代码(尤其是在较大的代码库中)不够清晰。

package mainimport "fmt"// namedReturn demonstrates named return values.
func namedReturn(x, y int) (result int) {result = x + yreturn
}// explicitReturn demonstrates explicit return values.
func explicitReturn(x, y int) int {return x + y
}func main() {// Named return valuessum1 := namedReturn(3, 5)fmt.Println("Named Return:", sum1)// Explicit return valuessum2 := explicitReturn(3, 5)fmt.Println("Explicit Return:", sum2)
}

在上面的示例程序中,我们有两个函数,分别是 namedReturn 和 explicitReturn。以下是它们的不同之处:

  • namedReturn 使用了名为 result 的命名返回值。虽然很清楚该函数返回了什么,但它在更复杂的函数中可能并不十分明显。
  • explicitReturn 直接返回结果。这样做更简单、更明确。

17: 尽可能将函数的复杂度降到最低

函数复杂度是指函数代码中复杂程度、嵌套程度和分支程度。保持函数复杂度较低可使代码更具可读性、易维护性,且不太容易出错。

package mainimport ("fmt"
)// CalculateSum returns the sum of two numbers.
func CalculateSum(a, b int) int {return a + b
}// PrintSum prints the sum of two numbers.
func PrintSum() {x := 5y := 3sum := CalculateSum(x, y)fmt.Printf("Sum of %d and %d is %d\n", x, y, sum)
}func main() {// Call the PrintSum function to demonstrate minimal function complexity.PrintSum()
}

在上述示例程序中:
我们定义了两个函数 CalculateSum 和 PrintSum,它们具有特定的职责。

  • CalculateSum 是一个简单的函数,它计算两个数字的和。
  • PrintSum 使用 CalculateSum 计算并打印 5 和 3 的和。、

通过保持函数简洁并专注于单一任务,我们维持了较低的函数复杂度,提高了代码可读性和可维护性。

18:避免变量影子化

当在一个较窄的范围内声明了一个与外部变量同名的变量,就会发生变量的影子化,此举可能导致意外的行为。它会隐藏同名的外部变量,使其在此范围内不可访问。避免在嵌套范围内对变量进行影子化,以免产生混乱。

package mainimport "fmt"func main() {// Declare and initialize an outer variable 'x' with the value 10.x := 10fmt.Println("Outer x:", x)// Enter an inner scope with a new variable 'x' shadowing the outer 'x'.if true {x := 5 // Shadowing occurs herefmt.Println("Inner x:", x) // Print the inner 'x', which is 5.}// The outer 'x' remains unchanged and is still accessible.fmt.Println("Outer x after inner scope:", x) // Print the outer 'x', which is 10.
}

19: 使用接口进行抽象

  • 抽象
    抽象是 Go 中一个基本概念,它允许我们定义行为,而无需指定实现细节。
  • 接口
    在 Go 中,接口是方法签名的集合。
    任何实现接口所有方法的类型隐式满足该接口。
    这使我们能够编写可以与不同类型一起工作的代码,只要它们遵守相同的接口即可。
package mainimport ("fmt""math"
)// Define the Shape interface
type Shape interface {Area() float64
}// Rectangle struct
type Rectangle struct {Width  float64Height float64
}// Circle struct
type Circle struct {Radius float64
}// Implement the Area method for Rectangle
func (r Rectangle) Area() float64 {return r.Width * r.Height
}// Implement the Area method for Circle
func (c Circle) Area() float64 {return math.Pi * c.Radius * c.Radius
}// Function to print the area of any Shape
func PrintArea(s Shape) {fmt.Printf("Area: %.2f\n", s.Area())
}func main() {rectangle := Rectangle{Width: 5, Height: 3}circle := Circle{Radius: 2.5}// Call PrintArea on rectangle and circle, both of which implement the Shape interfacePrintArea(rectangle) // Prints the area of the rectanglePrintArea(circle)    // Prints the area of the circle
}
}

20:避免将库包和可执行文件混在一起

在 Go 中,关键是要在包和可执行文件之间保持明确的分离,以确保代码的简洁性和可维护性。

myproject/├── main.go├── myutils/└── myutils.go
  • myutils/myutils.go:
package myutilsimport "fmt"// Exported function to print a message
func PrintMessage(message string) {fmt.Println("Message from myutils:", message)
}
  • main.go:
// Main program
package mainimport ("fmt""myproject/myutils" // Import the custom package
)func main() {message := "Hello, Golang!"// Call the exported function from the custom packagemyutils.PrintMessage(message)// Demonstrate the main program logicfmt.Println("Message from main:", message)
}

在上述示例中,我们有两个单独的文件:myutils.go 和 main.go。
myutils.go 定义了一个名为 myutils 的自定义包。它包含一个导出的函数 PrintMessage,用于打印一条消息。
main.go 是使用其相对路径(“myproject/myutils”)导入自定义包 myutils 的可执行文件。
main.go 中的 main 函数从 myutils 包中调用 PrintMessage 函数并打印一条消息。这种职责分离使代码保持井然有序且易于维护。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/835040.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

官宣:vAsterNOS正式发布!开放网络操作系统免费试用!

近期&#xff0c;vAsterNOS&#xff08;设备模拟器&#xff09;正式发布&#xff0c;可以满足用户快速了解 AsterNOS、体验实际操作、搭建模拟网络的需求&#xff0c;可运行在GNS3、EVE-NG等网络虚拟软件中。 AsterNOS 网络操作系统是星融元为人工智能、机器学习、高性能计算、…

算法 第56天 动态规划16

583 两个字符串的删除操作 给定两个单词 word1 和 word2 &#xff0c;返回使得 word1 和 word2 相同所需的最小步数。 每步 可以删除任意一个字符串中的一个字符。 def minDistance(word1,word2):dp[[0]*(len(word2)1) for _ in range(len(word1)1)]for i in range(len(word…

前端面试:项目细节问题(已工作|给大家做个分享)

1、介绍介绍整个项目的开发流程&#xff1f; 答&#xff1a;我面试时的回答&#xff1a;首先&#xff0c;产品、项目经理和甲方一起开会确认需求&#xff0c;产品编写需求文档&#xff0c;并画出原型图贴在需求文档上&#xff1b;然后&#xff0c;产品、项目经理、甲方、技术、…

vCenter 7.3证书过期无法登录处理方法

登录报错&#xff1a;如下图 Exception in invking authentication handler [SSL: CERTIFICATE_VERIFY_FAILED] certificate vertify failed: certificate has expired(_ssl.c:1076) 处理方法1&#xff1a;推荐&#xff0c;可行 登录vCenter控制台&#xff0c;AltF3切换至命令…

antdVue 自定义table列配置

最近做项目的时候需要对页面的table进行列配置的需求 子组件 <div><a-modaltitle"列配置" :visible"visible" :closable"false" :footer"null"width"800px" height"448px"><div><a-row>…

.DevicData-P-XXXXXXX勒索病毒数据怎么处理|数据解密恢复

导言&#xff1a; 在网络空间&#xff0c;每一次病毒或勒索软件的涌现都如同一次科技界的“地震”。而.DevicData-P-XXXXXXX勒索病毒&#xff0c;正是这场“地震”中的一股不可忽视的力量。它以其独特的加密技术和狡猾的传播方式&#xff0c;给全球的个人和企业带来了前所未有…

office 官方下载地址

office 官方下载地址&#xff1a;https://answers.microsoft.com/en-us/msoffice/forum/all/useful-microsoft-download-links-for-office-direct/7bcaa971-9493-44b6-a1ba-0db4c6957c47 Office 2013 Home and Student Russian https://officeredir.microsoft.com/r/rlidO15C2…

在linux里登录远程服务器

在linux里登录远程服务器。在虚拟终端里输入命令&#xff1a; ssh 远程服务器ip -l username 然后输入登录密码&#xff0c;就可以登录到远程服务器的命令行界面。登录方便&#xff0c;字体也可以在本地机的虚拟终端里设置得大一点。 下面是一张截屏图片。

QTableWidget导入导出excel

导入导出excel的方式有很多种&#xff0c;我这里使用的表格类型为CSV&#xff0c;这样可以不依赖那些office软件 导入&#xff1a; QList<QStringList> data;QString file_name QFileDialog::getOpenFileName(this,tr("Read file"),"",tr("fil…

系统Cpu利用率降低改造之路

系统Cpu利用率降低改造之路 一.背景 1.1 系统背景 该系统是一个专门爬取第三方数据的高并发系统&#xff0c;该系统单台机器以每分钟400万的频次查询第三方数据&#xff0c;并回推给内部第三方系统。从应用类型上看属于IO密集型应用,为了提高系统的吞吐量和并发&#xff0c;…

音频数字信号I2S一些知识理解

(1)I2S单向基本传输需要几根线传输音频信号? 3根线 LRCK SCLK(也叫BLK) DATA(单向) (2)如何理解I2S MASTER或者SLAVE的模式&#xff1f; codec的i2s作为slave mode,LRCK和SCLK来自于soc主控端,codec端自动检测MCLK和LRCK codec的i2s作为master mode,codec通过MCLK LRCLKDIV…

【Delphi 爬虫库 5】HTTP响应状态码说明详解(Response Status Code)

HTTP&#xff08;Hypertext Transfer Protocol&#xff09;是互联网上应用最广泛的协议之一&#xff0c;用于在客户端和服务器之间传输数据。在HTTP通信中&#xff0c;服务器在接收到客户端请求后&#xff0c;会返回一个HTTP响应&#xff0c;其中包含一个状态码&#xff0c;这个…

CSS-浮动

float (浮动) 作用&#xff1a;盒子的顶点是一样的&#xff0c;具备行内块的特征&#xff0c;能设置宽高 属性&#xff1a;float 属性值&#xff1a;left 浮动在网页左边 right 浮动在网页右边 .a{width: 100px;height: 100px;float:left;background-color: red;}.b…

drawio 网页版二次开发(1):源码下载和环境搭建

目录 一 说明 二 源码地址以及下载 三 开发环境搭建 1. 前端工程地址 2. 配置开发环境 &#xff08;1&#xff09;安装 node.js &#xff08;2&#xff09;安装 serve 服务器 3. 运行 四 最后 一 说明 应公司项目要求&#xff0c;需要对drawio进行二次开发&…

VUE 或 Js封装通用闭包循环滚动函数

1、vue3 闭包滚动函数的使用 js 调用也基本雷同 // 滚动Tab组件const scoreTabRef ref()// 滚动的选项const scrollOption ref({// 滚动的Dom元素scrollDom: null,// 滚动的时间间隔scrollInterval: 1500,// 滚动的距离scrollSep: 100,// 滚动历时时间scrollDuration: 10…

Microsoft Project使用简明教程

一.认识Microsoft Project Microsoft Project 是微软公司开发的项目管理软件&#xff0c;用于规划、协调和跟踪项目的进度、资源和预算&#xff0c;如下图所示&#xff0c;左边是任务的显示&#xff0c;右边是一个日程的显示图&#xff0c;最上方的长方形处在我们项目设定日程…

Python 3 中zip()函数的用法

1 创作灵感 我们在阅读代码的时候&#xff0c;经常会看到zip函数&#xff0c;有的时候还和循环在一起用&#xff0c;今天举几个例子测试一下该函数的用法 2.应用举例 &#xff08;1&#xff09;定义了两个列表一个是num,一个是letter (2)使用zip可以把num列表和letter列表中…

typescript 模拟数据统一封装 增删改查 例

功能&#xff1a;定义一个操作数据的库&#xff0c;支持 Mysql、Mssql、MongoDb 要求&#xff1a;Mysql、Mssql、MongoDb功能一样&#xff0c;都有add、update、delete、get方法 注意&#xff1a;约束统一的规范、以及代码重用 解决方案&#xff1a;需要约束规范所以要定义接口…

【高阶数据结构】图 -- 详解

一、图的基本概念 图 是由顶点集合及顶点间的关系组成的一种数据结构&#xff1a;G (V&#xff0c; E)。其中&#xff1a; 顶点集合 V {x | x属于某个数据对象集} 是有穷非空集合&#xff1b; E {(x,y) | x,y属于V} 或者 E {<x, y> | x,y属于V && Path(x, y…

pdf编辑软件,四款软件让你轻松玩转PDF编辑!

在信息爆炸的当今时代&#xff0c;PDF格式文档因其跨平台、不易被篡改的特性而深受大家喜爱。然而&#xff0c;如何高效地编辑PDF文档却成为许多人的难题。今天&#xff0c;我将为大家推荐四款实用的PDF编辑软件&#xff0c;让你轻松玩转PDF编辑&#xff0c;告别繁琐操作&#…