golang开发实践
先处理错误避免嵌套
尽量避免重复
重复的代码想办法用一个函数包起来
type binWriter struct { w io.Writer size int64 err error } // Write writes a value to the provided writer in little endian form. func (w *binWriter) Write(v interface{}) { if w.err != nil { return } if w.err = binary.Write(w.w, binary.LittleEndian, v); w.err == nil { w.size += int64(binary.Size(v)) } }
func (g *Gopher) WriteTo(w io.Writer) (int64, error) { bw := &binWriter{w: w} bw.Write(int32(len(g.Name))) bw.Write([]byte(g.Name)) bw.Write(int64(g.AgeYears)) return bw.size, bw.err }
也可以通过type-switch处理不同类型(可以利用在配置表差不多有相同字段)
func (w *binWriter) Write(v interface{}) { if w.err != nil { return } switch v.(type) { case string: s := v.(string) w.Write(int32(len(s))) w.Write([]byte(s)) case int: i := v.(int) w.Write(int64(i)) default: if w.err = binary.Write(w.w, binary.LittleEndian, v); w.err == nil { w.size += int64(binary.Size(v)) } } } func (g *Gopher) WriteTo(w io.Writer) (int64, error) { bw := &binWriter{w: w} bw.Write(g.Name) bw.Write(g.AgeYears) return bw.size, bw.err }
函数适配器
函数 handler
包含了业务的逻辑和错误处理,下来将错误处理单独写一个函数处理
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 }
先写最重要的代码
查询文档
go install golang.org/x/tools/cmd/godoc@latest
godoc -http=:6060
给代码写文档注释
命名尽可能简洁
保持包的独立性
如果一个包对另一个包有强依赖性的时候,例如下列的parse,draw
// Parse the text into an executable function. f, err := parser.Parse(text) if err != nil { log.Fatalf("parse %q: %v", text, err) } // Create an image plotting the function. m := drawer.Draw(f, *width, *height, *xmin, *xmax) // Encode the image into the standard output. err = png.Encode(os.Stdout, m) if err != nil { log.Fatalf("encode image: %v", err) }
代码中 Draw
方法接受了 Parse
函数返回的 f
变量,从逻辑上看 drawer
包依赖 parser
包
parser
包:
type ParsedFunc struct { text string eval func(float64) float64 } func Parse(text string) (*ParsedFunc, error) { f, err := parse(text) if err != nil { return nil, err } return &ParsedFunc{text: text, eval: f}, nil } func (f *ParsedFunc) Eval(x float64) float64 { return f.eval(x) } func (f *ParsedFunc) String() string { return f.text }
import "image" // Function represent a drawable mathematical function. type Function interface { Eval(float64) float64 } // Draw draws an image showing a rendering of the passed Function. func Draw(f Function) image.Image {
用接口解耦
避免内部的并发
函数do暴露给对外,内部自己处理并发
func do(job string) error { fmt.Println("doing job", job) time.Sleep(1 * time.Second) return errors.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) } } }
创建一个传递退出状态的通道来避免Goroutine的泄露
func broadcastMsg(msg string, addrs []string) error { errc := make(chan error) quit := make(chan struct{}) defer close(quit) for _, addr := range addrs { go func(addr string) { select { case errc <- sendMsg(msg, addr): fmt.Println("done") case <-quit: fmt.Println("quit") } }(addr) } for _ = range addrs { if err := <-errc; err != nil { return err } } return nil }