Go的12个最佳实践

参考视频:https://www.youtube.com/watch?v=8D3Vmm1BGoY

1. 避免过多的嵌套

先判断错误,错误后直接返回,而不要在错误的if块内写后续逻辑

2. 尽量避免重复

3. 使用type switch来处理类型

调用方不用关心v的类型,不用提前转换

switch v.(type) {
    case string:
    	...
    case int:
    	...
    default:
    	...
}

4. function adapter

func init() {
    http.HandleFunc("/", handler)
}

func handler(w http.ResponseWriter, r *http.Request) {
    err := doThis()
    if err != nil {
    	http.Error(w, err.Error(), http.StatusInternalServerError)
        log.Printf("handling %q: %v", r.RequestURI, err)
        return
    }

    err := doThat()
    if err != nil {
    	http.Error(w, err.Error(), http.StatusInternalServerError)
        log.Printf("handling %q: %v", r.RequestURI, err)
        return
    }
    ...
}

可以改为:

func init() {
	http.HandleFunc("/", errorHandler(betterHandler))
}

func errorHandler(f func(http.ResponseWriter, *http.Request) error) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
    	err := f(w,r)
        if err != nil {
        	http.Error(w, err.Error(), http.StatusInternalServerError)
            log.Printf("handling %q: %v", r.RequestURI, err)
        }
    }
}

func betterHandler(w http.ResponseWriter, r *http.Request) error {
	if err := doThis(): err != nil {
    	return fmt.Errorf("doing this: %v", err)
    }
    if err := doThat(): err != nil {
    	return fmt.Errorf("doing that: %v", err)
    }
    return nil
}

类似python中的decorator,解耦了流程处理和错误处理

5. 把关键的代码先写

License, tags, package documentation, import语句,使用空格隔开分组 剩余的代码,先写关键代码,最后写helper fuction和types

6. 写注释,文档

在package之前写一段当前package的文档,不需要空行 需要exported的部分,应该写明白

7. 命名越短越好

不需要特别长的名字,尽量找到最短的能够自我说明的名字 MarshalIndent比MarshalWithIndention更好

8. package内使用多个文件

  • 避免太长的文件
  • 将测试和代码分离
  • 将package的文档分离,可单独创建一个doc.go文件专门用于写文档

9. 将package做成能够使用go get获取

模块化,将可复用的代码放入package中

10. 使用需要的

# 原始
func (g *Gopher) WriteToFile(f *os.File) (int64, error) {...}

# 使用更抽象的ReaderWriter interface
func (g *Gopher) WriteToReaderWriter(rw io.ReadWriter) (int64, error) {...}

# 既然使用了interface那么可以仅使用我们需要的
func (g *Gopher) WriteToWriter(f io.Writer) (int64, error) {...}

11. 保持独立的package的独立性

12. 避免在api中使用concurrency

应该编写同步的api,然后使用concurrent的方式去调用

fuc doConcurrently(job string, err chan error) {
	go func() {
    	fmt.Println("doing job", job)
        time.Sleep(1 * time.Second)
        err <- erros.New("something went wrong!")
    }()
}

func main() {
	jobs := []string{"one", "two", "three"}
    errc := make(chan error)
    for _, job := range jobs {
    	doConcurrently(job, errc)
    }
    for _ = range jobs {
    	if err := <-errc; err!=nil {
        	fmt.Println(err)
        }
    }
}
func do(job string) error {
	fmt.Println("doing job", job)
    time.Sleep(1 * time.Second)
    return erros.New("something went wrong!")
}

func main() {
	jobs := []string{"one", "two", "three"}
    errc := make(chan error)
    for _, job := range jobs {
    	go func(job string) {
        	errc <- do(job)
        }(job)
    }
    for _ = range jobs {
    	if err := <-errc; err != nil {
        	fmt.Println(err)
        }
    }
}
comments powered by Disqus