For standalone (non-Android) applications, what is...
# announcements
n
For standalone (non-Android) applications, what is everyone's preferred DI library?
g
I believe Dagger2 is still the best option if you need fast and most scalable solution
c
I prefer Guice, been using it for years. Less boilerplate and much easier to set up, and being a reflection-based lib makes it possible to use it for a simple plugin system. Only downside is that it has long startup times, but for long-running applications this is easy to ignore.
g
What kind boilerplate do you have in Dagger?
You also can use Dagger for plugins using multi binding
But I agree, reflection based can have some advantages, but still on scale annotation processing can give significant advantages about speed and compile-time checks
c
For boilerplate, Guice has no need for Components or anything like it, and the modules are a bit cleaner as well.
For plugins, you need to recompile the core in order to support a new module, which wouldn’t work in a framework setting. You can take your framework, a bunch of modules compiled elsewhere, and still get them to build a single graph entirely at runtime (perhaps through configuration files or classpath scanning to find these modules)
But I totally agree, the nature of reflection DI does mean you lose some performance and safety, but it is by no means a deal breaker IMO
g
But if you have only one scope for whole application and only constructor injections, you component will have one method. I agree, this is can be considered as boilerplate, but also it’s just more explicit
It depends of course, I agree
s
fwiw I don’t use anything but constructor injection, field injection is scary stuff at times lol
d
So... given the power of Kotlin's
lazy
, what are the advantages of a DI library over using lazy `val`s (singletons) and `fun`s (providers) in a custom Dependencies class?
g
Because DI has nothing related to lazy
And DI is not about singletons
lazy property for dependency creation is actually against DI rules. You should provide dependencies from outside, do not create them on the same class
Of course you can write Service Locator using delegate properties to request required dependencies, but again, this is dependency inversion but not DI
and has the main problem of service locator: class has hard dependency on service locator implementation
d
Ahhh - I wasn't clear
I mean create a Dependencies class with all the injectable resources as vals and inject one into the other into the lazy initialisers. They're external and injectable
g
so every class that need any dependency now has hard dependency to Dependencies
which is also not really good solution, especially on big scale
it’s even worse that service locator
d
Other way around
g
DI is:
Copy code
class SomeClass(val dep1: SomeDep1, val dep2: SomeDep2)
or the same with method injection (which has a lot of problems, especially in Kotlin with nullability)
So you have inversion of control and class doesn’t have any hard dependencies to any Injector, ServiceLocator and so on
Other way around
could you show an example
d
Copy code
open class Dependencies() {
    open val serviceB: ServiceB by lazy {
        ServiceB( serviceA)
    }
    open val serviceA: ServiceA by lazy {
        ServiceA()
    }
g
Okay, where is dependency injection here?
d
Sorry, more haste, less speed 😄
Obvs ServiceA instance is lazily created when it needs to be injected into B, (or is used elsewhere)
Class can easily be overridden by subclasses to inject test dependencies
g
This is manual dependency injection, yes
now add 10 more dependencies
and some graph of them
d
Sure, but if I compare with my Guice definitions, it's hardly, if any, more complicated
g
But I agree, Kotlin simplifies manual dependency injection code a lot
yes, sorry, just now get your point, yes, this may be a valid approach of course, but mostly on relatively small scale
we use similar approach on one of our small projects
but looks that when this graph will grow it will be really hard to support it
d
Yes, true
g
Sure, but if I compare with my Guice definitions, it’s hardly, if any, more complicated
If you use constructor with
@Inject
annotation and your modules mostly just maps interfaces to implementations (or even don’t do this) than Guice or Dagger will save a lot of boilerplate for you
d
🤔 Maybe I missed a trick with Guice! Checking...
g
Because main boilerplate in this case will be defining new dependencies in your Dependencies class, invoking constructors and passing dependencies, which completely eliminated by Guice reflections or Dagger code generations
Of course DI framework adds additional configuration overhead and on small scale this overhead can be higher than saved boilerplate
but if you have relatively big graph you save a lot of code, especially if you use mostly constructor injections with dependencies that exist on the same graph (which is should be you preferred way to work with dependencies)
d
Ah, yes. I used the
@Provides
annotation in Guice, which yields a Module almost identical to that in the lazy example
g
Yes,
@Provides
significantly increase amount of code, but in most cases you actually can avoid this
even for complicated cases when you need dynamic parameter as dependency, it’s possible to use
@AssistedInject
for this
d
IIRC, my main driver in using
@Provides
was to allow test contexts to override certain dependencies. Is that possible using
@Inject
?
g
But you can override test dependencies, but you need module for that
Because you can override @Inject dependency with @Provides dependency
Do you need this for some integration tests?
👍 1
Also I’m not so familiar with Guice, but this is reflection, so you probably can just replace it on reflection level without actual updating of graph