Golang idioms
Contents
Funcy interface
- Single function interfaces can be supplanted by function types, the most popluar example being http.Handler & http.HandlerFunc
type Handler interface { ServeHTTP(w http.ResponseWriter, r *http.Request) } type HandlerFunc(w http.ResponseWriter, r *http.Request) func func (f HandlerFunc) ServeHTTP(w http.ResponseWriter, r *http.Request) { f(w, r) }
Wrapper Adapter
- Wrapping preserves interfaces while adding functionality
func Wrap (h http.Handler) http.Handler { return &wrapper{ handler: h} } type wrapper struct { handler http.Handler } func (w* wrapper) ServeHTTP(w http.ResponseWriter, r *http.Request) { // Do something before w.handler.ServeHTTP(w, r) // Do something after } func main() { myHandler := NewHandler(); http.Handle("/", Wrap(myHandler)) }
Limit goroutines
- Use a buffered channel to limit creation of simultaneously active goroutines.
var ( concurrent = 5 limit = make(chan struct{}, concurrent) ) func doWork(i int) { limit <- struct{}{} // block until slot is released by existing goroutine go func { defer func() { <-limit // release a slot } log.Println(i) time.Sleep(1*time.Second) } } func main() { for i:=0; i < 10000; i++ { doWork(i) } }
Dynamic dispatch
-
A combination of a single function interface and dynamic dispatch can be used for validation during entity creation / updation. For instance:
-
Let the entity do it's own validation by having it implement a Validator interface
type Validator interface { func Validate(v ...interface{}) error } type User struct { Email string } // checks for errors in u optionally against v func (u *User) Validate(v ...interface{}) error { }
-
A function Validate can then select a Validator depending on the type of the interface.
func Validate(u interface{}, v ...interface{}) error { uu := reflect.ValueOf(u) err := selectValidatorType(uu.Type())(uu, v...) return err } var ( validatorType = reflect.TypeOf(new(Validator)).Elem() ) type validatorFunc func(u reflect.Value, vv ...interface{}) error func selectValidatorType(t reflect.Type) validatorFunc { if t.Implements(validatorType) { return selectValidator } else { return errorValidator } } func selectValidator(u reflect.Value, v ...interface{}) error { if u.Kind() == reflect.Ptr && u.IsNil() { return DSErr{When: time.Now(), What: "Nil Value"} } m := u.Interface().(Validator) err := m.Validate(v...) if err != nil { return DSErr{When: time.Now(), What: err.Error()} } return nil } func errorValidator(u reflect.Value, v ...interface{}) error { return DSErr{When: time.Now(), What: "Not a Validator"} }