高质量编程与性能调优 | 青训营笔记

92 阅读2分钟

这是我参与「第三届青训营 -后端场」笔记创作活动的第2篇笔记

一.什么是高质量:编写的代码能够达到正确可靠,简明清晰的目标

其中要注意:考虑完备各种边界条件;做到异常情况处理和稳定性保证;易读易维护

二.如何高质量编写go代码

1.代码格式:

使用go语言官方提供的工具gofmt,可以自动格式化go语言代码为官方统一风格也可以使用go语言官方提供的goimports,自动增删依赖的包的引用,并将依赖包按照字母排序分类

2.注释

注释必须做到:解释代码作用;解释代码如何去做;解释代码实现原因;解释代码在什么情况下会出错

3.命名规范

(1).简洁 例如:for循环中的变量不需要过长

    for i := 0; i < len(intArr); i++ {
	intArr[i] = rand.Intn(100)
    }

(2).缩略词全大写,但是当其位于变量开头且不需要到处时,使用全小写

(3).变量距离其被使用的地方越远则需要携带越多的上下文信息

(4).函数名尽量不携带报名的上下文信息

(5).包名只有小写字母,不包含大写字母和下划线等信息。不能与标准库同名。

4.流程控制

1.避免嵌套
     if aaa{                          if aaa {
         return 1                       return 1
     }else{            =======>       }
         return 2                       return 2
     }
                                
  这样就避免了else的使用
  
2.尽量保持代码路径为最小缩进
        for i := 0; i < len(score); i++ {
                fmt.Printf("请输入第%d个元素", i+1)
                fmt.Scanln(&score[i])
        }
        for i := 0; i < len(score); i++ {
                fmt.Println(score[i])
        }
        for index, value := range score {
                fmt.Printf("i=%v v=%v", index, value)
        }
        for i, v := range score {
                fmt.Printf("i=%v v=%v", i, v)
        }

5.错误和异常处理

1.简单错误:指仅出现一次的错误,在其他地方不需要捕获该错误

(1).使用errors.New创建匿名变量来直接表示简单错误

if a>=5 {
   return errors.New("error")
}

(2).使用fmt.Errorf(会进行一次格式化)

if a == 0 {
   // 将参数进行一次格式化,格式化后的字符串放入 error 中 
   return fmt.Errorf("数据 %d 不合法", a), 0 
}

2.错误的Wrap和Unwrap

Wrap为我们提供了,可以一个error嵌套另一个error功能,我们可以根据嵌套的error序列,生成一个error错误跟踪链,也可以理解为错误堆栈信息,便于我们跟踪调试
a := errors.New("原始错误a")
b := fmt.Errorf("Wrap了一个错误%w", a)

3.错误判定:使用errors.Is来判断一个错误是否为特定错误,该方法可以判断错误链上所有的错误是否含有特定的错误

if errors.Is(err, ErrFoo) { 
   return 0, fmt.Errorf("tragedy: %w", err) 
}

用errors.As来获取特定种类的错误

if errors.As(err, &bar) {}

注: errors.Is 和 errors.As 会尝试以递归的方式解包错误来找到匹配项

  1. recover:此函数只能在defer的函数中使用且嵌套无法生效recover() 方法调用后,会捕获到当前抛出的异常,并进行返回,如果没有异常,则返回 nil。

三.性能优化

1.使用Benchmark工具

2.slice预分配内存,尽可能在使用make()初始化时提供容量信息

一个切片由3部分组成:指针、长度和容量。指针指向底层数组,长度代表slice当前的长度,容量代表底层数组的长度

        func PreAlloc(size int){
	data := make([]int,0,size)
	for k := 0; k < size; k++ {
		data=append(data,k )
	}

3.map预分配内存

        func PreAlloc(size int){
	data := make(map[int]int,size)
	for k := 0; k < size; k++ {
		data[k]=1
	}

4.字符串处理

字符串拼接建议使用string.Builder

5.使用空结构体节省内存

6.atomic包

type mutexCounter struct{
	i int32
	m sync.Mutex
}
func MutexAddOne(c*mutexCounter){
	c.m.Lock()
	c.i++
	c.m.Unlock()
}