Hacker News new | past | comments | ask | show | jobs | submit login

As someone who has to use Guice at work, and not by choice, the following critique best sums up my thoughts on Guice.

http://omaritech.blogspot.com/2014/03/why-google-guice-is-ev...

Am I in the minority here? Do most people really think that the Guice framework approach is a good idea?




The linked post is framed as a criticism of Guice but it's actually a criticism of Dependency Injection.

DI is one of those architectural tradeoffs that makes one thing easier (swapping out an alternate implementation) while making another thing harder (following execution paths), and trades imperative, prescriptive, top-down style with declarative, "loosely coupled", componentized, bottom-up style made possible by an overarching helper and externalized decision points that kinda-sorta act like configuration.


It's really just a criticism of dependency injection /frameworks/.

Manually wiring dependencies by calling constructors is still dependency injection. This also has the upside that the compiler will check you have satisfied all dependencies, and you can more easily trace execution.

Frameworks like Guice/Spring are often used to hide lots of wiring boilerplate but come at a price of losing typesafety, adding more magic, and hiding some code smells. If your wiring is getting complex perhaps you need more modularity.


Your criticisms seem to only apply to runtime dependency injection frameworks. That's why I like Dagger, and recommended it elsewhere in this thread; it's compile-time dependency injection. So you do get type safety, the compiler does check that you have satisfied all dependencies, etc. Check it out if you haven't used it before.


You're right, most of the downsides are specific to runtime wiring. I haven't used dagger. It does compile time magic doesn't it?


Yes. If you do something wrong in your wire-up then the code won't even compile. Definitely look into it. Like you, I have a healthy distrust of DI frameworks because of the many errors that get pushed to runtime, but Dagger's not bad.


(Disclosure: I work on Dagger in my 20% at Google.)

What's more, Dagger gives you (compile-time) insights into the graph of dependencies.

For example, we've hooked up our internal code-search tool to give you Dagger cross-references, so if you (e.g.) click on a parameter to an @Inject constructor, you'll see the place that provides that object.

Also, if you click on the @Component, you'll see a visualization of the entire graph.

We're working on surfacing these hooks in the open source repo, which would allow IDEs like Eclipse to gain this functionality as well.


In my experience "swapping out an alternate implementation" happens so rarely that it isn't really worth the additional architectural complexity and making debugging harder.

Seems to me like DI (at least the heavy-handed Spring XML style of it) is a solution looking for a problem, but I've only worked at one company that used it heavily so I'd be happy for people to try to change my view.


"Swapping out an alternate implementation" sometimes means you shipped a bunch of components and the customer (who is not you, and doesn't necessarily have a direct line to you) can configure their software to enable or disable features.

By having externalized some of the tight coupling into some config file (like Spring's XMLs), they can go in and say that the AuthorizationFilter is OAuth20Filter instead of OAuth10Filter, or maybe their CookieParser is StrictRFCCompiantCookieParser instead of the WhateverGoesCookieParser.

The same would be doable if you compiled all this into code and only exposed some different config parameter that says "enforceRFC6265Cookies=true/false", but then the customer couldn't use some consultant company's SuperSpecialCustomCookieParser class in its stead, whereas with Spring they can.

But say none of this applies; DI is often used in testing. You can inject a mock class with next-to-no effort without having to figure out what to do about all of those StorageManagers whose hardcoded dependency is your ActuallyRealDatabaseStorageManagerImpl.


> In my experience "swapping out an alternate implementation" happens so rarely that it isn't really worth the additional architectural complexity and making debugging harder.

It happens all the time if you test your code with mocks and stubs.


You aren't. 'lightweight' and 'dependency injection' reads like an oxymoron to me. The whole 'implicit dependency injection' seems as terrible an idea as getting an injection sounds. But it's out there so it's always interesting to hear more about it one way or the other.


The issue I have with DI frameworks is the annotation scanner scanning all jars in the classloader and annotation magic.

It seems that vanilla Guice does not need to fire up an annotation scanner that hits every jar, so that's good. It's been years for me, but I seem to recall Spring Framework does a brutal amount of jar crawling/annotation scanning by default. Unfortunately, it's very easy to corrupt Guice with annotation scanning - Netflix Governator will introduce a scanner for its magic annotations, for example.

As far as annotation magic is concerned, I think having an agreed-upon convention for DI can really help to keep things manageable and easy while still being able to unit test/inject mocks. For example, my team tries to only inject on constructor args. This makes it a no-nonsense affair to factor out features for a unit under test by passing in mocks for injected dependencies.

Testing complex systems, for me, is the killer app for DI - if I am writing something I need to test, I abhor the new keyword and FactoryFactories in my production code.


I don't know your codebase, but I'm going to propose that your (and that blogger's) projects could be even messier without Guice. As the blogger points out, the "simple alternative to DI" is:

     BillingService billingService = new BillingService(new BillingModule());
...which looks fine with just a couple layers of depth but becomes pathological with hundreds of objects, especially when they have different lifecycles. At some point the extra transparency you get from manual injection is overwhelmed by the sheer quantity of boilerplate.

To compensate, DI-less solutions end up with service discovery or "bag" classes like DropWizard's io.dropwizard.setup.Environment. Aside from the loss of encapsulation, what if you want to extend the framework with new components? You end up exposing services by setting attributes with text keys...

Having a messy Guice-based project doesn't mean it would be pretty without Guice.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: