7 ms·
Dependency Injection with Go
- chewxy 12y agoDoes anyone think this is a little stoppy (there isn't a Go equivalent to "unpythonic")?
- Mithaldu 12y ago> there isn't a Go equivalent to "unpythonic" Of course there is. :) Python does good marketing and rephrased an existing word, but the pair you're looking for is: idiomatic / unidiomatic
- saj1th 12y agoFound it interesting that inject wires up the object graph and runs only once on application startup. Curious to know your thoughts around why this lib could be considered non-idiomatic
- mutatio 12y ago"Typically the main() function would call the various init functions like InitMongoService" Why muddy your main() when init() exists? See: http://golang.org/ref/spec#Package_initialization http://golang.org/ref/spec#Package_initialization
- sagichmal 12y agoAnd why use InitXxx at all, when you can create an initialized, locally-scoped MongoService with a constructor, and pass it to the things that need it?
- mutatio 12y agoI was referring to Go's magic func init() Putting that in your files, let's say: db.go func init() => handle global DB's... templates.go func init() => handle HTML templates... settings.go func init() => load some settings... All fired automatically prior to your applications main() entry point WITHOUT needing to pollute main() with initDB(); initTemplates(); initSettings() etc. etc.
- nostrademons 12y agoThey're trying to pass the objects created at startup into other objects, so that dependencies are explicit and can be mocked out in unit tests. You can't do this with init(), which always takes no arguments and returns no values.
- sagichmal 12y ago> I was referring to Go's magic func init() I know you were; I'm arguing that components shouldn't need an explicit initialization step, but rather they should be initialized as part of their `NewFoo` constructor.
- argon81 12y agoWent down this path already. It was OK for a while with a small codebase, but the problem is that you need to give up your simple direct Go code for magic reflection. I discovered there are better and more idiomatic ways to do this type if thing without resorting to dependency injection.
- sagichmal 12y agoStrongly agree. A verbose `main` carries the benefit of being explicit and unambiguous. Components whose constructors take all of their dependencies are simple to reason about and straightforward to test. The code described in the article seems like it's burdened with patterns from other languages and ecosystems, and which are pretty nonidiomatic Go: "AppLoader"? The complexity of their final solution seems to be a result of not challenging those patterns and assumptions.
- blt 12y agoYeah. I think the fear of a verbose `main` is wrong. It's OK for complicated code to look complicated. If your app needs to initialize a lot of complex network dependencies at startup, then maybe your startup routine is going to have a lot of code. No big deal. Software is complex. We all repeat, and try to follow, the mantra of writing simple code that does one thing. We succeed a lot of the time. But sometimes, we just need to do something f'n complicated. Better to express the complexity in type safe, debuggable code that everyone understands, than to hide it behind some framework. I think developers should be more accepting of a code base that's 95% clean and 5% messy. "The perfect is the enemy of the good."
- elithrar 12y ago> I discovered there are better and more idiomatic ways to do this type if thing without resorting to dependency injection. Agreed. Write a constructor[1] for your "app" or "context" type, and pass it around as needed. You both get to avoid globals and any magic dependency injection. As a bit of shameless self-promotion, I wrote an article about how to achieve this when writing HTTP handlers in Go: http://elithrar.github.io/article/custom-handlers-avoiding-globals/ http://elithrar.github.io/article/custom-handlers-avoiding-g... - hint: create a struct type that accepts a pointer to your app struct and your handler type/http.Handler, or create your handlers as methods on your app type. [1]: http://www.jerf.org/iri/post/2929 http://www.jerf.org/iri/post/2929
- pjmlp 12y agoAnd thus the path to Go2EE slowly starts....
- lmm 12y agoJust as with Java, if you keep the language simple then the complexity still has to go somewhere.
- iand 12y agoI'm interested in real examples of dependency injection being useful outside of hooking up testing objects. Anyone got any links to interesting examples?
- eknkc 12y agoMartini (https://github.com/go-martini/martini https://github.com/go-martini/martini) uses DI for everything. I's supposed to be freaking slow though. But it might not be a real world problem.
- iand 12y agoI guess I should have been clearer - I mean examples where you actually want different dependencies for the same application. All uses of DI that I have seen have a single configuration that basically never changes. The DI is used for hooking up test objects, but not for reconfiguring the production app.
- fishnchips 12y agoI honestly don't think there even was a problem to be solved. Constructors could just accept their dependencies (interfaces) as parameters.
- shellac 12y agoOn larger code bases it can help a lot. You get to a point where you need that config object over in some random function and you start weaving it through constructors. At which point a simple DI system really appeals. DI code also tends to be more pleasant to test in my experience since it promotes loose coupling.
- ExpiredLink 12y agoQuite the contrary as repeatedly experienced in Java land.
- fishnchips 12y agoI believe there are other ways: http://commandcenter.blogspot.com/2014/01/self-referential-functions-and-design.html http://commandcenter.blogspot.com/2014/01/self-referential-f... And you should probably avoid situations where you suddenly need config objects in 'random places'. This could be a symptom of chaotic API design. But then again, whatever works for you.