To manage the responsibility of dependency creation, each Go application should have a service locator that is responsible for construction and lookup of dependencies.
Asking for dependencies solves the issue of hard coding, but it also means that the dic needs to be passed throughout the application. Passing the injector breaks the Law of Demeter.
To remedy this, you could inject to a service only the dependency it needs.
- More Reusable Code
- More Testable Code
- More Readable Code
see example folder, you could run with
run go run example/example.go
and go run example/example_struct.go
type mailer struct {
Logger string `dic:"log"`
Transport Transport `dic:"transport.sendmail"`
}
mailer = new(mailer)
cnt := container.New()
cnt.Register("output_writer", os.Stdout)
cnt.Register("log", log.New, reference.New("output_writer"), "", log.Ldate|log.Lmicroseconds|log.Lshortfile)
cnt.Register("transport.sendmail", SendmailNew /*, lot of arguments*/)
cnt.inject(mailer)
mailer.Transport.Send()
mailer.Logger.Println("hello log")
Injection of dependencies into controllers
cnt := container.New()
cnt.Register("logger", log.New /*, lot of arguments*/)
cnt.Register("template.path", "template/")
cnt.Register("template", abcTemplate.new, reference.New("template.path"), reference.New("logger"))
cnt.Register("transport.sendmail", SendmailNew /*, lot of arguments*/)
cnt.Register("mailer", MailerNew, reference.New("transport"), "[golangit] ")
// injecting into controller
cnt.Register("controller_home", func (logger Logger, mailer Mailer) {/*do something*/}, reference.New("logger"), reference.New("mailer"))
cnt.Register("controller_admin", func (tpl Template, mailer Mailer) {/*do something*/}, reference.New("template"), reference.New("mailer"))
type TestStruct struct {
dbName string
logger Logger
}
cnt := container.New()
// Storing pareter
cnt.Register("log.filename", "error.log")
cnt.Register("db.name", "logger-database")
// storing service
cnt.Register("logger", LoggerNew, reference.New("log.filename"))
cnt.Register("context", &TestStruct{}, reference.New("db.name"), reference.New("logger"))
test := cnt.Get("context").(TestStruct)
// now struct has .dbName = 'logger-database' and the logger is an object wht the filename injected
Register
stores functions, structs or parameters into the service locator.
Get
resolves dependencies and return the value.
Inject
Everything is injected with lazy injection.
cnt := container.New()
if env == "prod" {
cnt.Register("transport.sendmail", SendmailNew /*, lot of arguments*/)
} else {
cnt.Register("transport.sendmail", StubMailNew)
}
Is possible to create a service usable only as dependency
def := definition.New(SendmailNew).setPublic(false)
cnt.Register("transport.sendmail", def)
All the service are served statically, this means that the service is executed only once.
def := definition.New(SendmailNew).setStatic(false)
cnt.Register("transport.sendmail", def)
cnt.Register("mail.prefix", "[golangit] ")
type mailer struct {
Sender MailSender `dic:"transport.sendmail"`
MailPrefix string `dic:"mail.prefix"`
}
mailer := &mailer
cnt.Inject(mailer) //mailer has now dependencies injected
mailer.Sender.Send()
This library has been developed with ginkgo a BDD Testing Framework for Go,
Install the dependencies:
go get -v -t ./...
go get -v github.com/onsi/ginkgo
go get -v github.com/onsi/gomega
go install -v github.com/onsi/ginkgo/ginkgo
Run all the bdd tests:
ginkgo -r
Symfony dependency injection container
@mikespook bind function
- alias
- Cli for debugging
- godoc
- improving injection