Search code examples
gobeego

Go prevent repetition


I am working on web app that is going to have multple models (and related tables) and they are obviously going to have a lot of similiar methods, the only difference is going to be the returned result type.

Examples:

package models
type User struct {...}
type Task struct {...}

func GetUser(uid string) (*User, error) {
  user := User{Id: id}
  if err := ORM.Read(&user); err != nil {
      return nil, err
  }
  return &user, nil
}

func GetTask(uid string) (*Task, error) {
  task := Task{Id: id}
  if err := ORM.Read(&task); err != nil {
      return nil, err
  }
  return &task, nil
}

and many many more...

in Controllers etc I am using it like:

user := models.GetUser(id)

I am not really sure how to solve this problem in Golang, any suggestions?

PS: I am using Beego Framework, if it helps.


Solution

  • Pass in the Model as an interface and expect the function to populate it. No need to return it. Basically you follow almost the exact paradigm that ORM.Read does.

    // GetByID loads the model for dst's concrete type based on its Id field using ORM.Read
    func GetByID(dst interface{}) error {
        if err := ORM.Read(dst); err != nil {
            return nil, err
        }
        return nil
    }
    

    This function expects you to create whatever model you are getting with the ID already set. It populates the model you pass in unless it returns an error.

    So usage as follows:

    user := User{Id: id}
    err := GetByID(&user)
    if err != nil {
        // handle error. model not populated
    }
    // user is ready to use
    
    task := Task{Id: id}
    err = GetByID(&task)
    ...
    

    As a future note, when you run into this sort of situation again, take note of the function type of the library call you are making. Often times, if it takes an interface{} like this, you can easily make a generic wrapper by doing the exact same thing.