Well, for starters, you've now created a dependency on the injector, which is valid but I find makes code substantially more confusing in the long-run. It also means that we could never EVER analyze your code statically to determine if your wiring was correct (that is, all dependencies are bound) because you've internalized the logic of the dependency fulfillment into a code-path instead of a declaration.

Also, you can't see the dependencies from without - say, from method signatures or otherwise, so it's harder to reason things through. You can always look at the code, but the constructor signature is now meaningless - it doesn't semantically include the dependencies - it includes a piece of infrastructure.

You're right - there's a bit more work to do from the outside with assisted injection in terms of maintaining dependency lists. But the clarity in the code I find to be valuable.

In practice, I don't find it that difficult to keep these things clear.

public class Foo {

  //private variables

  // factory interface near constructor.
  public interface Factory {
    Foo create(Consumable someValue);
  }

Foo(Dependency dep, OtherDependency dep2, @Assisted Consumable someValue) {
    ...
  }

}

This way, if I need to change that list, I change the two nearly beside each other - easy to see.

The other problem is that with assisted injection, you now can separate different KINDS of injected things - collaborating services, configuration values, consumables, and pass them around differently. Consumables (value types, local differences) have quite different life cycles from services, etc. So if you do what you're suggesting, you absolutely start to need to play scope games. Because if you have values that aren't just created once for each request being part of the object's lifecycle, you now have to create narrower and narrower @Scopes in order to support these shorter lifetimes.

Sometimes things really just need to be on the call-stack. And having a factory break as part of a factory call is a far clearer error (and stack-trace) than a deep failure of injection. They might be systemically equivalent, but their meaning to the coder is different, and finding the errors later are quite different.

Generally, anything you might want to use Guice as a factory for, I tend to recommend using assisted injection, because it creates separate, traceable factories for each type. Yes, it's "more work" in one sense - but I think it's a better organization of code.

Injecting the injector is something that is possible, and sometimes necessary to support legacy code. But I think the problems outweigh the ease you think it'll buy you - especially for down-stream maintainers.

Christian

On 7 Nov 2012, at 15:50, Gili wrote:

Hi,

I've been using Guice over the past couple of years and it worked quite
well for me. However, every once in a while I run across a recurring
problem and I'd like to get your feedback on it. The problem arises when I
run across a constructor that requires a mix of Guice-injected and
user-supplied values. For example:

public MyService(UriInfo uriInfo, String userName)

This is precisely the use-case AssistedInject is meant to address but the more I run into this code, the more I am leaning towards using the Service Locator pattern [1] instead. AssistedInject requires me to construct and
bind one factory per class. This means that every time I add/remove a
class, I need to add/remove a binding. It means every time I add/remove user-supplied parameters, I need to also add/remove parameters from the factory. Initially it didn't seem like a big deal but time I've questioned the cost/benefit of this approach. The resulting code is harder to read and
maintain, and for what? Nowadays I replace:

@Inject
public MyService(First first, Second second, Third third, @Assisted
String userName)

with

@Inject
public MyService(Injector injector, String userName)
{
this.first = injector.getInstance(First.class);
this.second = injector.getInstance(Second.class);
this.third = injector.getInstance(Third.class);
}

and simply inject classes on-demand using injector. Essentially I'm using
Guice as a Service Locator. The way I see it:

- The classes/constructors are a lot easier to maintain.
- No need to enumerate the injected parameters in the Javadoc. I've
always found it annoying to have the Javadoc cluttered with parameters the user doesn't really care about (besides the fact it was a lot of work to
keep such documentation up-to-date).
- This still implements the inversion of control pattern so the code is
easily testable.
- The dependencies might not be listed as constructor parameters, but
they still visible near the top of the constructor.
- To clarify, I only advocate this approach when mixing injected and
user-supplied parameters. I still use normal constructor injection when all
parameters come from Guice.

So really, what's the harm? I'd love to get your feedback on this.

Thanks,
Gili

[1] http://martinfowler.com/articles/injection.html#UsingAServiceLocator

--
You received this message because you are subscribed to the Google Groups "google-guice" group. To view this discussion on the web visit https://groups.google.com/d/msg/google-guice/-/HSTXZ7On-acJ.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to [email protected]. For more options, visit this group at http://groups.google.com/group/google-guice?hl=en.

--
You received this message because you are subscribed to the Google Groups 
"google-guice" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to 
[email protected].
For more options, visit this group at 
http://groups.google.com/group/google-guice?hl=en.

Reply via email to