Got provides a declarative way of composing dependencies. It integrates nicely with the common constructor patterns in Go applications.
The main advantage of got is removing the need to manually pass references around when instantiating types that depend on one another.
go get github.com/eriicafes/gotgot has a very small API surface. You typically only need to use two functions from this library.
Constructor return values are cached the first time they are requested and future calls return the cached value instead of re-running the constructor. See Transient constructors to opt out of caching.
Constructors are global variables with their names prefixed with
Getas a convention.
// printer.go
package main
import "github.com/eriicafes/got"
type Printer interface {
Print(string) string
}
var GetPrinter = got.Using(func(c *got.Container) Printer {
return &CapsPrinter{}
})
// CapsPrinter is an implementation of Printer interface
type CapsPrinter struct{}
func (*CapsPrinter) Print(s string) string {
return strings.ToUpper(s)
}Retrieve an instance from a constructor by calling GetXXX.From(container)
// office.go
package main
import "github.com/eriicafes/got"
type Office struct {
Printer Printer
}
var GetOffice = got.Using(func(c *got.Container) *Office {
return &Office{
Printer: GetPrinter.From(c),
}
})Create a container to hold cached instances.
When calling a constructor, any dependencies it has will also be cached, ensuring that shared dependencies use the same instance.
// main.go
package main
import "github.com/eriicafes/got"
func main() {
c := got.New()
office := GetOffice.From(c)
// or using the From function from got
office := got.From(c, GetOffice)
office.Printer.Print()
}Transient constructors create a new instance each time it is requested.
If you want your constructor to act as a transient, use GetXXX.New(container) to opt out of caching its return value.
// office.go
package main
import "github.com/eriicafes/got"
type Office struct {
Printer Printer
}
var GetOffice = got.Using(func(c *got.Container) *Office {
return &Office{
// a new printer is created each time
Printer: GetPrinter.New(c),
}
})Constructors may return two values, for example an instance and an error. Use got.Using2 to create such a constructor.
// bad_office.go
package main
import "github.com/eriicafes/got"
var GetBadOffice = got.Using2(func(c *got.Container) (*Office, error) {
return nil, fmt.Errorf("failed to create office")
})You can mock a constructor using got.Mock or got.Mock2.
// main.go
package main
import "github.com/eriicafes/got"
type MockPrinter struct{}
var GetMockPrinter = got.Using(func(c *got.Container) Printer {
return &MockPrinter{}
})
func (*MockPrinter) Print(s string) string {
return fmt.Sprintf("mocked %s", s)
}
func main() {
mc := got.New()
got.Mock(mc, GetPrinter, GetMockPrinter.New(mc))
office := GetOffice.From(mc)
office.Printer.Print()
}Go prevents you from creating circular dependencies as long as you maintain the convention and use global vars as constructors.