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"}
    }