Skip to content

Instantly share code, notes, and snippets.

@packrat386
Last active April 6, 2018 21:17
Show Gist options
  • Save packrat386/ae58241024c869545a3b02174d5d09c9 to your computer and use it in GitHub Desktop.
Save packrat386/ae58241024c869545a3b02174d5d09c9 to your computer and use it in GitHub Desktop.
// pacakge iface is a comparison of two different interfaces for a client
// to a simple REST API. Assume I had access to both `GET /resource`, which
// returns a list of all resources (which may be empty), and `GET /resource/:id`,
// which returns the resource with the given ID, OR a 404 if no resource
// has that ID. Additionally, since these are http requests they could also
// fail with some other non-200 status code for a variety of reasons, and
// we want to be able to distinguish between a request that failed and a
// resource not existing.
//
// The issue I find is that using pointers is nicer in the case of getting a
// single resource, since you can return to indicate that you successfully
// found nothing. However, for the collection of resources, the interface is
// equally usable (in part because you can return nil for a slice), and
// using pointers where we don't need to is needlessly complex. And to make
// things even more difficult, to use pointers for one but not the other
// seems likely to confuse the user.
package iface
import (
"errors"
"fmt"
)
// ErrNotFound indicates a request succeeded
var ErrNotFound = errors.New("not found")
// Resource is some REST Resource
type Resource struct {
Data string
}
// Concrete returns only the resource itself
type Concrete interface {
// GetOne returns a single Resource if it exists. If no resource
// with that ID is found it returns ErrNotFound. If the request
// fails for a different reason it returns a different error. If
// it returns an error the Resource is just a zero-valued Resource
GetOne(id string) (Resource, error)
// GetAll returns all the resources. If there were no resources
// for some reason it returns an empty slice. If the request
// fails it returns an error. If it returns an error then the
// []Resource is nil.
GetAll() ([]Resource, error)
}
func UseConcreteOne(c Concrete) error {
resource, err := c.GetOne("someid")
if err == ErrNotFound {
fmt.Println("/resource/someid doesn't exist")
return nil
} else if err != nil {
fmt.Println("Something broke!")
return err
}
fmt.Println("Got what we wanted: ", resource.Data)
return nil
}
func UseConcreteAll(c Concrete) error {
resources, err := c.GetAll()
if err != nil {
fmt.Println("Something broke~")
}
for _, r := range resources {
fmt.Println("Got what we wanted: ", r.Data)
}
return nil
}
// Pointer returns only pointers to the resource
type Pointer interface {
// GetOne returns a single Resource if it exists. If no resource
// with that ID is found, it returns nil and no error. If the
// request fails it returns an error and the *Resource is nil
GetOne(id string) (*Resource, error)
// GetAll returns all the resources. If there were no resources
// for some reason it returns an empty slice. If the request
// fails it returns an error. If it returns an error then the
// []*Resource is nil
GetAll() ([]*Resource, error)
}
func UsePointerOne(p Pointer) error {
resource, err := p.GetOne("someid")
if err != nil {
fmt.Println("Something broke!")
return err
}
if resource == nil {
fmt.Println("/resource/someid doesn't exist")
return nil
}
fmt.Println("Got what we wanted: ", resource.Data)
return nil
}
func UsePointerAll(p Pointer) error {
resources, err := p.GetAll()
if err != nil {
fmt.Println("Something broke~")
}
for _, r := range resources {
fmt.Println("Got what we wanted: ", r.Data)
}
return nil
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment