唐汉网站建设,视频制作报价表,如何制作网址,wordpress 分页链接一、简介
高质量编程是指以高标准和良好实践来编写可读、可维护、可测试和性能等方面的优秀表现的代码。
各种边界条件是否考虑完备异常情况处理、稳定性保证易读易维护
#xff08;一#xff09;编码原则 从指令的角度考虑#xff0c;开发中应如何编码#xff0c;才能减…一、简介
高质量编程是指以高标准和良好实践来编写可读、可维护、可测试和性能等方面的优秀表现的代码。
各种边界条件是否考虑完备异常情况处理、稳定性保证易读易维护
一编码原则 从指令的角度考虑开发中应如何编码才能减少执行的指令。各种语言特性和语法各不相同但高质量编程遵循的原则是一致的如下
简洁性代码应该简洁明了避免冗余和复杂的逻辑。简洁的代码更易于理解、调试和维护。可读性代码应该易于阅读和理解。使用有意义的变量和函数命名遵循一致的代码风格添加适当的注释和文档以提高代码的可读性。一致性在编写代码时应遵循一致的命名规范、代码风格和代码组织结构。一致的代码风格使得代码更易于理解和维护。模块化将代码划分为模块或函数每个模块或函数只负责一个明确的任务。模块化的代码更易于测试、重用和维护。错误处理合理处理错误和异常情况。避免忽略错误而是采取适当的错误处理机制例如返回错误值或抛出异常。依赖管理使用Go模块管理依赖项确保代码的可重复性和可维护性。可以使用Go Modules来管理项目的依赖。测试编写测试是保证代码质量的重要手段。编写单元测试和集成测试覆盖代码的各个功能和边界情况。文档编写清晰、准确的文档包括代码注释、函数说明和项目文档等。良好的文档可以帮助其他开发人员理解和使用代码。并发安全性在多线程环境中需要确保代码的并发安全性。可以使用Go语言提供的互斥锁Mutex或通道Channel等机制来实现。
二如何编写高质量的Go代码 1. 代码格式
gofmt
gofmt是Go语言官方提供的一个命令行工具用于格式化Go代码。它会自动调整代码的缩进、空格、括号位置等以确保代码的一致性和可读性。
在命令行中可以使用以下命令来运行gofmt工具
gofmt -w 文件或目录
其中-w选项表示将格式化后的代码直接写回源文件如果不使用-w选项则gofmt会将格式化后的代码输出到标准输出。
例如要格式化名为main.go的文件可以运行以下命令
gofmt -w main.go
如果要格式化整个项目目录下的所有Go文件可以运行以下命令
gofmt -w .
需要注意的是gofmt工具会直接修改源文件因此在运行之前建议先备份代码以防止意外修改。
此外还可以使用一些编辑器或IDE中的插件如GoLand、Visual Studio Code的Go插件等来自动触发gofmt工具的格式化操作。这样可以在保存文件时自动进行代码格式化进一步提高开发效率。
goimports
goimports也是一个Go语言官方提供的工具它是在 gofmt的基础上增加了自动导入功能。除了格式化代码外goimports还会自动检测并添加缺失的导入语句删除未使用的导入语句并按照一定的规则对导入语句进行排序并分类。 2. 注释
在Go语言中注释是用来对代码进行说明和解释的文本。Go语言支持两种类型的注释单行注释和多行注释。
单行注释以//开头用于注释单行代码或单行说明。
// 这是一个单行注释
fmt.Println(Hello, World!) // 打印Hello, World!
多行注释以/*开头以*/结尾用于注释多行代码或多行说明。
/*
这是一个多行注释
可以跨越多行。
*/
fmt.Println(Hello, World!)
除了用来对代码进行说明注释还可以用来生成文档。在Go语言中可以使用特殊格式的注释来生成文档这种注释被称为文档注释或文档注解。
文档注释以/*开头以*/结尾并且在每行注释前添加一个*。文档注释可以包含一些特殊的标记如param、return等用于描述函数的参数和返回值。
/*
calculateSum函数用于计算两个整数的和。param a 第一个整数
param b 第二个整数
return 两个整数的和
*/
func calculateSum(a, b int) int {return a b
}可以使用go doc命令来查看代码中的文档注释。
go doc 包名.函数名
例如要查看calculateSum函数的文档注释可以运行以下命令
go doc 包名.calculateSum
注释是编写清晰、易读的代码的重要组成部分。良好的注释可以帮助其他开发人员理解代码的意图和功能并且可以用来生成文档以供参考。因此在编写代码时建议使用注释来解释和说明代码的逻辑和功能。 3. 命名规范
命名是代码规范中很重要的一部分统一的命名规则有利于提高的代码的可读性.
Go在命名时以字母a到Z或a到Z或下划线开头后面跟着零或更多的字母、下划线和数字(0到9)。Go不允许在命名时中使用、$和%等标点符号。
Go是一种区分大小写的编程语言。因此Manpower和manpower是两个不同的命名。当命名包括常量、变量、类型、函数名、结构字段等等以一个大写字母开头如Group1那么使用这种形式的标识符的对象就可以被外部包的代码所使用客户端程序需要先导入这个包这被称为导出像面向对象语言中的 public命名如果以小写字母开头则对包外是不可见的但是他们在整个包的内部是可见并且可用的像面向对象语言中的 private
变量命名
和结构体类似变量名称一般遵循驼峰法首字母根据访问控制原则大写或者小写但遇到特有名词时需要遵循以下规则
如果变量为私有且特有名词为首个单词则使用小写如 apiClient其它情况都应当使用该名词原有的写法如 APIClient、repoID、UserID错误示例UrlArray应该写成 urlArray 或者 URLArray若变量类型为 bool 类型则名称应以 Has, Is, Can 或 Allow 开头
var isExist bool
var hasConflict bool
var canManage bool
var allowGitHook bool
接口命名
命名规则基本和上面的结构体类型。 单个函数的结构名以 “er” 作为后缀例如 Reader , Writer 。
type Reader interface {Read(p []byte) (n int, err error)
}
文件命名
尽量采取有意义的文件名简短有意义应该为小写单词使用下划线分隔各个单词。
my_test.go
包命名package
保持package的名字和目录保持一致尽量采取有意义的包名简短有意义尽量和标准库不要冲突。包名应该为小写单词不要使用下划线或者混合大小写。
package demopackage main
4. 控制流程
流程控制是每种编程语言控制逻辑走向和执行次序的重要部分。
Go 语言的常用流程控制有 if 和 for而 switch 和 goto 主要是为了简化代码、降低重复代码而生的结构属于扩展类的流程控制。
Go语言if else分支结构
在Go语言中关键字 if 是用于测试某个条件布尔型或逻辑型的语句如果该条件成立则会执行 if 后由大括号{}括起来的代码块否则就忽略该代码块继续执行后续的代码。
if condition { // do something
}
如果存在第二个分支则可以在上面代码的基础上添加 else 关键字以及另一代码块这个代码块中的代码只有在条件不满足时才会执行if 和 else 后的两个代码块是相互独立的分支只能执行其中一个。
if condition {// do something
} else {// do something
}
如果存在第三个分支则可以使用下面这种三个独立分支的形式
if condition1 {// do something
} else if condition2 {// do something
} else {// catch-all or default
}
else if 分支的数量是没有限制的但是为了代码的可读性还是不要在 if 后面加入太多的 else if 结构如果必须使用这种形式则尽可能把先满足的条件放在前面。
Go语言switch case语句
表达式不需要为常量甚至不需要为整数case 按照从上到下的顺序进行求值直到找到匹配的项如果 switch 没有表达式则对 true 进行匹配。
Go语言改进了 switch 的语法设计case 与 case 之间是独立的代码块不需要通过 break 语句跳出当前 case 代码块以避免执行到下一行示例代码如下
var a hello
switch a {
case hello:fmt.Println(1)
case world:fmt.Println(2)
default: fmt.Println(0)}//代码输出 1上面例子中每一个 case 均是字符串格式且使用了 default 分支Go语言规定每个 switch 只能有一个 default 分支。
Go语言for循环结构
使用循环语句时需要注意的有以下几点
左花括号{必须与 for 处于同一行。Go语言中的 for 循环与C语言一样都允许在循环条件中定义和初始化变量唯一的区别是Go语言不支持以逗号为间隔的多个赋值语句必须使用平行赋值的方式来初始化多个变量。Go语言的 for 循环同样支持 continue 和 break 来控制循环但是它提供了一个更高级的 break可以选择中断哪一个循环如下例
for j : 0; j 5; j {for i : 0; i 10; i {if i 5 {break JLoop } fmt.Println(i)}
}JLoop:// ...上述代码中break 语句终止的是 JLoop 标签处的外层循环。 5. 错误和异常处理
当涉及到错误和异常处理时Go语言采用了一种不同于其他语言的方法。Go语言中的错误处理是通过返回错误值来完成的而不是使用异常机制。
简单错误
在Go语言中errors.New()函数用于创建一个新的错误对象。它接收一个字符串参数作为错误的描述信息并返回一个错误类型的值。通过判断错误对象实例来确定具体错误类型。
err : errors.New(something error)
fmt.Errorf() 创建 error 接口错误对象
err : fmt.Errorf(发生了错误%s, reason)
通过调用 fmt.Printf 函数并给定占位符 %s 就可以打印出某个值的字符串表示形式。对于其他类型的值来说只要我们能为这个类型编写一个 String 方法就可以自定义它的字符串表示形式。
而对于 error 类型值它的字符串表示形式则取决于它的 Error 方法。在上述情况下fmt.Printf 函数如果发现被打印的值是一个 error 类型的值那么就会去调用它的 Error 方法。fmt 包中的这类打印函数其实都是这么做的。
顺便提一句当我们想通过模板化的方式生成错误信息并得到错误值时可以使用 fmt.Errorf 函数。该函数所做的其实就是先调用 fmt.Sprintf 函数得到确切的错误信息再调用 errors.New 函数得到包含该错误信息的 error 类型值最后返回该值。
错误的Wrap和Unwrap
在Go语言中标准库中的errors包提供了Wrap和Unwrap函数是指错误处理机制中的用于错误的包装和解包。
错误包装是指在处理错误时将原始错误包装在新的错误中以提供更多的上下文信息。这样做可以保留原始错误的堆栈信息并将新的错误与之关联。错误解包是指从包装的错误中提取出原始错误。这样做可以在需要时获取原始错误的详细信息。
目前Go标准库中提供的用于wrap error的API有fmt.Errorf和errors.Join。fmt.Errorf最常用fmt.Errorf也支持通过多个%w一次打包多个error下面是一个完整的例子
func main() {err1 : errors.New(error1)err2 : errors.New(error2)err3 : errors.New(error3)err : fmt.Errorf(wrap multiple error: %w, %w, %w, err1, err2, err3)fmt.Println(err)e, ok : err.(interface{ Unwrap() []error })if !ok {fmt.Println(not imple Unwrap []error)return}fmt.Println(e.Unwrap())
}示例运行输出如下
wrap multiple error: error1, error2, error3
[error1 error2 error3]
我们看到通过fmt.Errorf一次wrap的多个error在String化后是在一行输出的。
errors.Join用于将一组errors wrap为一个error。 下面是用errors.Join一次打包多个error的示例
func main() {err1 : errors.New(error1)err2 : errors.New(error2)err3 : errors.New(error3)err : errors.Join(err1, err2, err3)fmt.Println(err)errs, ok : err.(interface{ Unwrap() []error })if !ok {fmt.Println(not imple Unwrap []error)return}fmt.Println(errs.Unwrap())
}这个示例输出如下
$go run demo2.go
error1
error2
error3
[error1 error2 error3]
我们看到通过errors.Join一次wrap的多个error在String化后每个错误单独占一行。 错误判定
errors.Is(err, target)判断err是否是target类型的错误返回布尔值。这个函数用于判断错误类型是否匹配。 if errors.Is(err, io.EOF) {fmt.Println(遇到了文件末尾)
} errors.As(err, target)将err转换为target类型的错误返回布尔值。这个函数用于将错误转换为特定类型的错误并进行相应的处理。 var n *net.OpError
if errors.As(err, n) {fmt.Println(遇到了网络错误, n)
}
异常panic和恢复recover用法
panic
1、内建函数 2、假如函数F中书写了panic语句会终止其后要执行的代码在panic所在函数F内如果存在要执行的defer函数列表按照defer的逆序执行 3、返回函数F的调用者G在G中调用函数F语句之后的代码不会执行假如函数G中存在要执行的defer函数列表按照defer的逆序执行这里的defer 有点类似 try-catch-finally 中的 finally 4、直到goroutine整个退出并报告错误
recover
1、内建函数 2、用来控制一个goroutine的panicking行为捕获panic从而影响应用的行为 3、一般的调用建议 a). 在defer函数中通过recever来终止一个gojroutine的panicking过程从而恢复正常代码的执行 b). 可以获取通过panic传递的error 简单来讲go中可以抛出一个panic的异常然后在defer中通过recover捕获这个异常然后正常处理。