First draft of dependency injection package#6
Conversation
|
You have run out of free Bugbot PR reviews for this billing cycle. This will reset on October 26. To receive reviews on all of your PRs, visit the Cursor dashboard to activate Pro and start your 14-day free trial. |
There was a problem hiding this comment.
Hi Miro,
I learned a bit Go while reading the code ;)
In Go, pointers that are not initialized reference to nil, however, this is not checked at the type level (read: compile time) and the runtime will just panic when dereferencing nil.
This perfectly allows to split service creation and injection! Especially it supports cyclic dependencies in a natural/direct way (because struct instances can be built step-by-step).
It is an interesting feature of Go that a struct can be augmented with an Interface - using a function. However, it is a bit confusing that Injectable is not explicitly mentioned (e.g. TestInjectableService has Inject but does not mention that it implements Injectable.
I asked Claude:
Go is statically typed, not duck-typed. However, it has a feature called interfaces that provides duck-typing-like flexibility at compile time.
I see now why there is no constructor injection. Because the Inject impl pulls all needed service dependencies out of the DI container. That is perfectly possible.
Thinking...
To the time of Google Guice I liked modules because they served as a central catalogue of services (easy to grasp for me).
I'm now wondering how this new DI concept works in practice - can one still get a clear picture of which services are available? The key to understanding and maintaining a good overview is having a central place for both the service keys and the service registrations at the container.
Update: dependency pointers must not change after injection. I guess that it is not a problem to have pointers to functions instead of structs? Or how are providers/factories modeled?
Overall this is a good approach that is very well tailored to the characteristics of the Go language! 👍
|
@danieldietrich I started a second round before I saw your PR. I think they are similar. |
msujew
left a comment
There was a problem hiding this comment.
I like both approaches to DI in this PR. It's rather difficult to pick one, but if I had to, I would go with the second one:
- The typing is more explicit, which also prevents to register "invalid" services. This is a real concern in the first approach.
- IMO the
Injectinterface function approach is very object-oriented and not very idiomatic to go. - Both approaches require some kind of casting in case an adopter overrides a service and wants to retrieve the type of that overriden service. As far as I can tell, there should be no runtime/compilation issues with that approach though.
First Attempt (commits 1 & 2)
Concepts:
InjectAllto inject dependenciesGetSecond attempt (commit 3)
Concepts:
Connectionis set only when the language server is started