Go的12个最佳实践
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)
}
}
}