There's been a lot of interest in doing assisted injection for Dagger. Let's all agree on the problem, propose some solutions, and decide whether that solution is worth its cost.
I have a class that gets some dependencies from the object graph, and other dependencies from a caller at runtime. It's like a Provider
that takes parameters.
public class ImageDownloader {
// Get these dependencies from the injector.
private final HttpClient httpClient;
private final ExecutorService executorService;
// Get these from the caller.
private final URL imageUrl;
private final ImageCallback callback;
...
}
It isn't that difficult though it is verbose. Fewer fields can be final, and it's possible to forget to call a setter. The fact that you need to inject a Provider
is ugly, and looks magical.
public class PhotoGallery implements ImageCallback {
@Inject Provider<ImageDownloader> imageDownloaderProvider;
public void showImage(URL imageUrl) {
ImageDownloader downloader = imageDownloaderProvider.get();
downloader.setImageUrl(imageUrl);
downloader.setCallback(this));
downloader.download();
}
...
}
This is what we're doing today at Square. We write the factory interface that we want:
public class ImageDownloader {
...
public interface Factory {
ImageDownloader create(URL imageUrl, ImageCallback callback);
}
}
And we also write a big fat method to implement the factory:
public class ImageModule {
...
@Provides public ImageModule.Factory(
final Provider<HttpClient> httpClientProvider, final Provider<ExecutorService> executorServiceProvider) {
return new ImageDownloader.Factory() {
public ImageDownloader create(URL imageUrl, ImageCallback callback) {
return new ImageDownloader(httpClientProvider.get(), executorServiceProvider.get(),
imageUrl, callback);
}
}
}
It's verbose, but not too verbose. My biggest problem with this approach is that it requires providers in the parameters, otherwise a factory that's used twice may behave strangely. In this case the two ImageDownloaders could share an HttpClient
, even if that class isn't singleton.
This solution isn't very discoverable. It's a design pattern that you may not come up with on your own.
Guice has an extension AssistedInject to solve this problem. They let you define a custom Factory
interface that replaces Provider
. You use @Assisted
annotations to match factory method parameters to the corresponding injections, plus a clumsy FactoryModuleBuilder
thing to register it all.
install(new FactoryModuleBuilder()
.implement(Payment.class, RealPayment.class)
.build(PaymentFactory.class));
I like the factory interface. The @Assisted
is tolerable, and the FactoryModuleBuilder
is quite unfortunate.
I want the Factory
interface most of all. Dagger needs to recognize a Factory
at an injection site, and implement it as we did above in the DIY solution. Basically it should work exactly like the DIY solution, only you don't need to write any @Provides
method.
Probably via an annotation:
public class ImageDownloader {
...
@AssistedFactory
public interface Factory {
ImageDownloader create(URL imageUrl, ImageCallback callback);
}
}
It's possible that we want to return say, ImageDownloaderImpl
instead of ImageDownloader
. All the APIs I imagine for this are horrible, and I don't think it's that useful. If you need it, use the DIY solution.
We probably don't need very much here. We don't need @Assisted
. In the unlikely event that two factory method parameters have the same type, just use @Named
to differentiate them.
I'm tempted to use ObjectGraph.plus()
, since it means we can use the existing code to inject the target object. Here's a sketch of code we could generate:
@Module(complete=false)
public class ImageDownloader$Factory$Module {
@Provides public ImageDownloader.Factory provideFactory(ObjectGraph base) {
return new ImageDownloader.Factory() {
ImageDownloader create(URL imageUrl, ImageCallback callback) {
ObjectGraph newGraph = base.plus(new ParametersModule(imageUrl, callback));
return newGraph.get(ImageDownloader.class);
}
};
}
@Module(entryPoints=ImageDownloader.class, complete=false);
static class ParametersModule {
private final URL imageUrl;
private final ImageCallback callback;
...
@Provides public URL provideImageUrl() {
return imageUrl;
}
@Provides public ImageCallback provideCallback() {
return callback;
}
}
}
Let's nail down the API. Remember, Dagger's raison d'être is its small size. If you want fancy features, use Guice.
Alternate , though similar, proposal
https://docs.google.com/document/d/1BioRZqm-pmhAPkFcKu_DD7Xslzw5bTx4ls00x0xCjSQ/view
It's not entirely dissimilar, though you mark the value type with an annotation that indicates what class is the factory interface, and two things are generated - a factory implementation that conforms to the factory interface and weaves together call-stack parameters and injected things, and a specialized InjectAdapter that links the factory interface to the implementation, to avoid needing to muck about with @Provides or module stuff - the loader will just load it, and the full graph analysis will support the case where an interface type has a loadable adapter.
It has the distinct advantage of being 90% implemented in a branch on my client.
Reading your proposal, I think I can make it so you don't even need @Parameter (@Assisted) annotations, but I left them in so one could have a value type that did "field injection" of parameters and to disambiguate quickly in the constructor which fields are injected. But it could be done without them.
Christian.
On 24 Jan 2013, at 19:35, Jesse Wilson wrote:
There's been a lot https://github.com/square/dagger/pull/148 of
interesthttps://plus.google.com/106927208431194134382/posts/DuVu1VjgeUwin
doing https://twitter.com/tbroyer/status/250636720234700800 assisted
injection for Dagger. Let's all agree on the problem, propose some
solutions, and decide whether that solution is worth its cost.
THE PROBLEM
I have a class that gets some dependencies from the object graph, and other
dependencies from a caller at runtime. It's like a Provider that takes
parameters.public class ImageDownloader {
// Get these dependencies from the injector.
private final HttpClient httpClient;
private final ExecutorService executorService;// Get these from the caller.
private final URL imageUrl;
private final ImageCallback callback;...
}PRIOR ART NO ASSISTANCE
It isn't that difficult though it is verbose. Fewer fields can be final,
and it's possible to forget to call a setter. The fact that you need to
inject a Provider is ugly, and looks magical.
public class PhotoGallery implements ImageCallback {
@Inject Provider imageDownloaderProvider;
public void showImage(URL imageUrl) {
ImageDownloader downloader = imageDownloaderProvider.get();
downloader.setImageUrl(imageUrl);
downloader.setCallback(this));
downloader.download();
}...
}DIY WITH PROVIDER METHOD
This is what we're doing today at Square. We write the factory interface
that we want:public class ImageDownloader {
...
public interface Factory {
ImageDownloader create(URL imageUrl, ImageCallback callback);
}
}And we also write a big fat method to implement the factory:
public class ImageModule {
...
@Provides public ImageModule.Factory(
final Provider httpClientProvider, final
Provider executorServiceProvider) {
return new ImageDownloader.Factory() {
public ImageDownloader create(URL imageUrl, ImageCallback callback) {
return new ImageDownloader(httpClientProvider.get(),
executorServiceProvider.get(),
imageUrl, callback);
}
}
}
It's verbose, but not too verbose. My biggest problem with this approach is
that it requires providers in the parameters, otherwise a factory that's
used twice may behave strangely. In this case the two ImageDownloaders
could share an HttpClient, even if that class isn't singleton.This solution isn't very discoverable. It's a design pattern that you may
not come up with on your own.
GUICE
Guice has an extension
AssistedInjecthttps://code.google.com/p/google-guice/wiki/AssistedInjectto
You received this message because you are subscribed to the Google Groups "Dagger Discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to dagger-discus...@googlegroups.com.
To post to this group, send email to dagger-...@googlegroups.com.
Nice doc. I like that we're mostly on the same page. Assuming code gen is disabled, how would Dagger discover that a Factory serves assisted injection?
import java.util.concurrent.ThreadFactory;
class Foo {
@Inject ThreadFactory threadFactory;
}
Does it look at the return type of ThreadFactory.newThread()
?
I don' think it's a popular feature, but with Guice's assisted injection it's possible to use one factory to construct multiple types:
interface Factory {
ImageDownloader newImageDownloader(URL url, Callback callback);
ImageUploader newImageUploader(URL url, Bitmap bitmap);
}
So, this notion of "if code generation is disabled" is a curious one. :D
Do we actually have a case where we want to support a reflection-only, won't load an adapter even if it's there?
The current implementation I have noodling around doesn't support reflection, and that's so that we can support the full loss of parameter name information in the class file, which you can do in annotation processing, but not with reflection. I know the recent implementation proposal added an annotation parameter to the @Assisted
annotation, precisely to work around all of this. I had really hoped to avoid having @Assisted("foo")
because of the verbosity of:
public Foobar(@Assisted("myLongStringName") String myLongStringName)
In general, I want to reduce duplication of semantic information where we can avoid it.
But this means that a factory implementation would only be useable without any further effort in a code-gen/loading situation.
That said, if you generated the factories, but not the adapters (not the current setup, but totally doable) then it would be trivial to do:
@Provides Foo.Factory blah(Foo.Factory.FactoryImpl impl) { return impl; }
… and that would just work. But in order for this to be even meaningful would be to separate the auto factory stuff into a separate project, since if you are already using the code-gen project, you might as well use the code-gen, right?
As to multiple factories, I think it's not an incredible savings to limit the factory to one create method. It's not a necessary limitation, though it does make things slightly easier. It's just what we choose to generate. I think it complicates the API a bit, in that it becomes more of a factory interface.
One further thought THe other thing I use @Parameter for is to make sure that I exclude it from the general adapter, so that an InjectAdapter isn't generated if it's a factory-built class. That is pretty crucial, because otherwise we'll generate adapters naively for anything with an @Inject class on it, and fail with errors if it doesn't have a suitable JIT-able constructor.
Christian.
On Thu, Jan 24, 2013 at 8:50 PM, Christian Gruber <cgr...@google.com> wrote:
Do we actually have a case where we want to support a reflection-only, won't load an adapter even if it's there?
Dagger needs to work without dagger-compiler. Code generation is clumsy. At Square our development builds don't use code generation.
One further thought THe other thing I use @Parameter for is to make sure that I exclude it from the general adapter, so that an InjectAdapter isn't generated if it's a factory-built class. That is pretty crucial, because otherwise we'll generate adapters naively for anything with an @Inject class on it, and fail with errors if it doesn't have a suitable JIT-able constructor.
I still think using ObjectGraph.plus() is the way to go. It avoids the need for @Parameter
altogether.
FACTORY RETURN TYPES
It's possible that we want to return say,
ImageDownloaderImpl
instead ofImageDownloader
. All the APIs I imagine for this are horrible, and I don't think it's that useful. If you need it, use the DIY solution.
MATCHING PARAMETERS
We probably don't need very much here. We don't need
@Assisted
. In the unlikely event that two factory method parameters have the same type, just use@Named
to differentiate them.
IMPLEMENTATION
I'm tempted to use
ObjectGraph.plus()
, since it means we can use the existing code to inject the target object. Here's a sketch of code we could generate:@Module(complete=false)
public class ImageDownloader$Factory$Module { @Provides public ImageDownloader.Factory provideFactory(ObjectGraph base) { return new ImageDownloader.Factory() { ImageDownloader create(URL imageUrl, ImageCallback callback) { ObjectGraph newGraph = base.plus(new ParametersModule(imageUrl, callback)); return newGraph.get(ImageDownloader.class); } }; } @Module(entryPoints=ImageDownloader.class, complete=false); static class ParametersModule { private final URL imageUrl; private final ImageCallback callback; ... @Provides public URL provideImageUrl() { return imageUrl; } @Provides public ImageCallback provideCallback() { return callback; } } }
NEXT STEPS
Let's nail down the API. Remember, Dagger's raison d'être is its small size. If you want fancy features, use Guice.
I don' think it's a popular feature, but with Guice's assisted injection it's possible to use one factory to construct multiple types:
interface Factory { ImageDownloader newImageDownloader(URL url, Callback callback); ImageUploader newImageUploader(URL url, Bitmap bitmap); }
On Thu, Jan 24, 2013 at 9:38 PM, Christian Gruber <christiane...@gmail.com> wrote:
I've been using it in development consistently now when I've been working with client code, even in development.
One example: Dagger's code generator is broken for incremental builds. If it generates Foo$InjectAdapter.java
and then you rename Foo.java
to Bar.java
, nothing garbage collects the old inject adapter. To work around this, you need to do clean builds.
ObjectGraph.plus() is really a narrower-lifetime scoping concept,
Yes, and these are narrower-lifetime objects. Otherwise we wouldn't need local variables to construct 'em.
I think going this way makes for a confusing API.
Yeah, the entire premise of assisted injection is confusing. I fear it's a power users feature.
On Thu, Jan 24, 2013 at 9:38 PM, Christian Gruber <christiane...@gmail.com> wrote:
I've been using it in development consistently now when I've been working with client code, even in development.One example: Dagger's code generator is broken for incremental builds. If it generates
Foo$InjectAdapter.java
and then you renameFoo.java
toBar.java
, nothing garbage collects the old inject adapter. To work around this, you need to do clean builds.
ObjectGraph.plus() is really a narrower-lifetime scoping concept,Yes, and these are narrower-lifetime objects. Otherwise we wouldn't need local variables to construct 'em.
On Thu, Jan 24, 2013 at 10:12 PM, Christian Gruber <christiane...@gmail.com> wrote:
Actually, it's quite common for factory parameters to be the same type, and I was hoping to avoid @Named or any other parameter that uses a value() if at all possible.
What would you use instead? Requiring code generation is a nonstarter.
...because it makes the end-user API much simpler and cleaner.
Please expand on this? Perhaps a programmer mistake that is eliminated by this API? For example, one problem with proposed API is that it uses @Inject
but unlike most classes with this annotation, it cannot be injected directly; it needs a factory.
On 24 Jan 2013, at 22:41, Jesse Wilson wrote:
On Thu, Jan 24, 2013 at 10:12 PM, Christian Gruber christiane...@gmail.com wrote:
Actually, it's quite common for factory parameters to be the same type, and
I was hoping to avoid @Named or any other parameter that uses a value() if
at all possible.What would you use instead? Requiring code generation is a nonstarter.
…because it makes the end-user API much simpler and cleaner.
Please expand on this? Perhaps a programmer mistake that is eliminated by
this API? For example, one problem with proposed API is that it uses
@Inject but unlike most classes with this annotation, it cannot be
injected directly; it needs a factory.
@Inject
is used, but @Inject
doesn't only imply that it can be injected directly. It means that certain members will be injected with dependencies, or that the marked constructor will be used to inject - but we already have cases of partially injected objects. Activities, and other things that don't follow the normal lifecycle use @Inject and are injected, but not all fields are injected, only marked ones. Dagger is partially involved, but not wholly involved in its creation/preparation.
So too with "assisted injection." I don't think using @Inject to signal that certain ingredients need to come from the graph is even slightly misleading.
What I mean by simpler and cleaner was more about it being uncluttered, however. Just having @Named("foo") annotations on parameters is pretty frustrating to read and organize. But that's an aesthetic thing.
One option is to simply say that there is no field injection on value types, and that the constructor takes N in-order parameters. On the implementation type, you mark those parameters that are to be satisfied by the injector, and all other are satisfyied, in-order, from the factory interface.
Something like
class Foo {
Foo(A a, @Graph B b, C c, @Graph D d) { … }
@Factory
static interface Factory {
Foo make(Aa, Cc);
}
}
That can be investigated reflectively and things positionally inferred. It could even allow for Graph-injected fields, but wouldn't use the @Inject annotation, so wouldn't be mistaken for injectables. That might look like:
class Foo {
@Graph E e;
Foo(A a, @Graph B b, C c, @Graph D d) { … }
@Factory
static interface Factory {
Foo make(Aa, Cc);
}
}
That's clean and simple, is consistent with the code-gen approach I was taking, but is also workable with reflection, requires no extra parameters on the annotations, and is pretty hard to misinterpret. Could go the other way and mark parameters instead of graph-injection sites, but I think this way is better, though it would probably do to think carefully about the right name. We want to associate it mentally with the graph, but not in a way that confuses it with the JSR-330 stuff.
Thoughts?
cheers,
Christian.
I'd rather use @Named()
, which is more explicit than parameter order. I don't like @Graph
, especially since we don't use it elsewhere.
A, B and C are from the graph. The factory provides two strings. You don't need @Named; any qualifier annotation would work.
public class Foo {
@Inject A a;
@Inject Foo(@Named("zip") String zip, @Named("city") String city, B b, C c) {
...
}
@AssistedFactory
interface Factory {
Foo make(@Named("city") String city, @Named("zip") String zip);
}
}
Foo$InjectAdapter.java
and then you rename Foo.java
to Bar.java
, nothing garbage collects the old inject adapter."--
You received this message because you are subscribed to the Google Groups "Dagger Discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to dagger-discus...@googlegroups.com.
Make @Assisted not qualified more. It's really my bad. Using @Named for naming assisted injections is better.https://github.com/FinamTrade/dagger/compare/master...assisted-injections
But generating factory is make some problem. I believe that It need to working with ReflectivePlugin without code generation.Second, when we annotated class with assisted injections it's more comfortable to use @Inject annotation. But InjectAdapted for this class should not be generated in this case, it's redundant.Third, I think that all dependencies should be define in Module. But generating custom Module looks like hack.
пятница, 25 января 2013 г., 4:35:44 UTC+4 пользователь Jesse Wilson написал:
I use assisted injection all over my code. Also if you can, take a look at Fred Faber's legprovider inside gin for an elegant robot legs problem solution.
Ok… but this makes for even more duplication in names all over the place - in short, clutter. But that's aesthetic on my part.
The big showstopper here, for me, is that this isn't an injectable class, but you're using @INject in a way that is misleadingly familiar, and you're using @Named in a way that is the INVERSE of how it's used in normal injectable classes. That is, this class looks like you are injecting @Named("zip") String zip
, etc.
@Named
is a qualifier in the JSR-330… it has a specific meaning for injection and you're using it for the non-injected case. In my own examples, I was at least using @Inject normally.
What happens if there is an @Named("zip")/String
bound in the graph? Obviously it wouldn't be injected, because this is factory managed, but it would read as if it should. We also need a signal to Dagger on the value class itself that this is NOT an injectable class. Otherwise Foo
would be injected with the dependencies A, B, C, zip/String, city/String regardless, and it would be massively inefficient to scan for all possible factories and test for membership before attempting to inject.
Lastly, I think having a non-JSR-330 annotation for this, something that cannot be confused with injection is crucial for readability. It's not a Named type key, which is what qualifiers are about - it's a factory-parameter call-stack dependency. Not the same concept, not the same label.
Consider yours:
public class Foo {
@Inject A a;
@Inject Foo(@Named("zip") String zip, @Named("city") String city, B b, C c) {
...
}
@AssistedFactory
interface Factory {
Foo make(@Named("city") String city, @Named("zip") String zip);
}
}
In light of an alternative
public class Foo {
@Inject A a;
@Inject Foo(@Param("zip") String zip, @Param("city") String city, B b, C c) {
...
}
@Factory
interface Factory {
Foo make(@Param("city") String city, @Param("zip") String zip);
}
}
This makes the annotations @Inject and @Named (if needed) have the same semantics, but the @Param is the signal that this is not directly injectable, but only partially. This would result in no attempt to JIT bind by reflection or generate an adapter, and all the signals are there to generate a factory properly.
That said, I still think this is cleaner.
public class Foo {
@Inject A a;
@Inject Foo(@Param String city, @Param String zip, B b, C c) {
...
}
@Factory
interface Factory {
Foo make(String city, String zip);
}
}
And as long as you assert order as the default interpretation absent the additional signal (we can make the disambiguation available, but not necessary), then it's fine. The generator can catch mistakes of this sort:
public class Foo {
@Inject A a;
@Inject Foo(@Param String zip, @Param String city, B b, C c) {
...
}
@Factory
interface Factory {
Foo make(String city, String zip);
}
}
where you have two strings in a row, and you invert them. Or we can simply say, if you have duplicated types so there's ambiguity, then it's an error if you don't have disambiguated, labelled parameters. I'd prefer an in-order interpretation, though. After all, that's how Java works - parameters are positional. And this makes what I think is the simplest, most readable code. And it doesn't require code generation, though you get more benefits if you use such.
@Factory
interface Factory {
Foo make(@Param("city") String city, @Param("zip") String zip);
}
You can use Factory
as the name of the annotation, or you can use Factory
as the name of the interface. You can't, unfortunately, use Factory
for both.
On 25 Jan 2013, at 10:31, Jesse Wilson wrote:
Nitpick:
@Factory
interface Factory {
Foo make(@Param("city") String city, @Param("zip") String zip);
}
You can use Factory as the name of the annotation, or you can use
Factoryas the name of the interface. You can't, unfortunately, use
Factory for both.
Heh. Whoops. True. @AutoFactory
? I don't like the word assist, mostly just because what is assisting what is ambiguous. Is it a factory that is assisted, or a factory assisting the injection. It's not THAT big a deal - I just wanted a less ambiguous word.
Alternately...
@dagger.Factory
interface Factory {
Foo make(@Param("city") String city, @Param("zip") String zip);
}
:)
Christian.
Fortunately, Dagger has short FQCNs:
@dagger.Factory
interface Factory {
THE PROBLEM
I have a class that gets some dependencies from the object graph, and other dependencies from a caller at runtime. It's like a
Provider
that takes parameters.public class ImageDownloader { // Get these dependencies from the injector. private final HttpClient httpClient; private final ExecutorService executorService; // Get these from the caller. private final URL imageUrl; private final ImageCallback callback; ... }
PRIOR ART
NO ASSISTANCE
It isn't that difficult though it is verbose. Fewer fields can be final, and it's possible to forget to call a setter. The fact that you need to inject a
Provider
is ugly, and looks magical.
public class PhotoGallery implements ImageCallback {
@Inject Provider<ImageDownloader> imageDownloaderProvider; public void showImage(URL imageUrl) { ImageDownloader downloader = imageDownloaderProvider.get(); downloader.setImageUrl(imageUrl); downloader.setCallback(this)); downloader.download(); } ... }
DIY WITH PROVIDER METHOD
This is what we're doing today at Square. We write the factory interface that we want:
public class ImageDownloader { ... public interface Factory { ImageDownloader create(URL imageUrl, ImageCallback callback); } }
And we also write a big fat method to implement the factory:
public class ImageModule { ... @Provides public
ImageModule.Factory( final Provider<HttpClient> httpClientProvider, final Provider<ExecutorService> executorServiceProvider) { return new ImageDownloader.Factory() { public ImageDownloader create(URL imageUrl, ImageCallback callback) { return new ImageDownloader(httpClientProvider.get(), executorServiceProvider.get(), imageUrl, callback); } } }
It's verbose, but not too verbose. My biggest problem with this approach is that it requires providers in the parameters, otherwise a factory that's used twice may behave strangely. In this case the two ImageDownloaders could share an
HttpClient
, even if that class isn't singleton.This solution isn't very discoverable. It's a design pattern that you may not come up with on your own.
GUICE
Guice has an extension AssistedInject to solve this problem. They let you define a custom
Factory
interface that replacesProvider
. You use@Assisted
annotations to match factory method parameters to the corresponding injections, plus a clumsyFactoryModuleBuilder
thing to register it all.
install(new FactoryModuleBuilder() .implement(Payment.class, RealPayment.class) .build(PaymentFactory.class));
I like the factory interface. The
@Assisted
is tolerable, and theFactoryModuleBuilder
is quite unfortunate.JESSE'S PROPOSAL
I want the
Factory
interface most of all. Dagger needs to recognize aFactory
at an injection site, and implement it as we did above in the DIY solution. Basically it should work exactly like the DIY solution, only you don't need to write any@Provides
method.
There's been a lot of interest in doing assisted injection for Dagger. Let's all agree on the problem, propose some solutions, and decide whether that solution is worth its cost.
THE PROBLEM
I have a class that gets some dependencies from the object graph, and other dependencies from a caller at runtime. It's like a
Provider
that takes parameters.public class ImageDownloader { // Get these dependencies from the injector. private final HttpClient httpClient; private final ExecutorService executorService; // Get these from the caller. private final URL imageUrl; private final ImageCallback callback; ... }
Good pull-back.
I have a work-in-progress candidate implementation. It hasn't solved the disambiguation question yet, as I wanted to consider the different ways. I've updated my doc to reflect some changes from this thread.
That said, some thoughts on each. One meta-question - how frequent are these use-cases? With the odd exception, I don't have strong enough feeling for others' usage to really push for any of these one way or another.
On 28 Jan 2013, at 14:11, Thomas Broyer wrote:
Back to basics, which use cases do we want to support? and to which
extent? (i.e. would there be easy workaround?)
- binding the factory to a sub-type of the declared return type (the Payment→RealPayment from Guice's wiki: https://code.google.com/p/google-guice/wiki/AssistedInject)
I think this is tricky, but worth considering. The main issue here is signaling.
- a factory interface from a 3rd-party library (e.g. java.util.concurrent.ThreadFactory)
This feels like it is best handled with an @Provides
method, but it could be done.
- a class that is injectable but, for some use case, we want to provide some dependencies through factory arguments (the class could possibly be injected directly elsewhere in the app)
Hmm. This one is tricky, because of how to "override" the naked class description and indicate to Dagger that it needs to manage with a factory.
The above three actually all seem like we may need two mechanisms for specifying factories. Firstly, a mechanism indicating that a value type is intended to be managed by a factory with some injectable pieces, by flagging these in the source somewhere. And secondly, a way to tell Dagger without any access to the source that a given type is factory managed, and should be treated as such, along with what parts of it are injectable and what parts are part of the factory method parameter.
- several methods in the factory with the same return type, dispatching to the appropriate constructor/members of the created class (the “Multiple factory methods for the same type” case from GUice's javadoc: http://google-guice.googlecode.com/svn/trunk/javadoc/com/google/inject/assistedinject/FactoryModuleBuilder.html )
So this seems reasonable - my branch looks for a single constructor, but if you had multiple constructors, it could simply satisfy the one with the correct available parameters.
- several methods in the factory with the same return type, being able to map them to different sub-types (the “More configuration options”, with fast-car → Porsche & clean-car → Prius, from Guice's javadoc: http://google-guice.googlecode.com/svn/trunk/javadoc/com/google/inject/assistedinject/FactoryModuleBuilder.html )
I think this can be done.
- should the assisted parameters be injectable to fields (and methods when/if we support them) or only as constructor arguments?
I like the option of both. My branch does both, but at some cost of "mental api load", though I think if we disambiguate cleanly without too much hassle, it's not that much extra mental burden. And it lets people choose how to structure their code.
Once we settled on the use-cases:
- how do we want to disambiguate arguments of the same type?
I put this in my doc, but I'll put it here
My preference is @Param(numeral)
(or @Arg
- it's shorter), or labels but only requiring @Arg(label) if there is ambiguity.
Christian.
Back to basics, which use cases do we want to support? and to which extent? (i.e. would there be easy workaround?)
- binding the factory to a sub-type of the declared return type (the Payment→RealPayment from Guice's wiki: https://code.google.com/p/google-guice/wiki/AssistedInject)
- a factory interface from a 3rd-party library (e.g. java.util.concurrent.ThreadFactory)
- a class that is injectable but, for some use case, we want to provide some dependencies through factory arguments (the class could possibly be injected directly elsewhere in the app)
- several methods in the factory with the same return type, dispatching to the appropriate constructor/members of the created class (the “Multiple factory methods for the same type” case from GUice's javadoc: http://google-guice.googlecode.com/svn/trunk/javadoc/com/google/inject/assistedinject/FactoryModuleBuilder.html)
- several methods in the factory with the same return type, being able to map them to different sub-types (the “More configuration options”, with fast-car → Porsche & clean-car → Prius, from Guice's javadoc: http://google-guice.googlecode.com/svn/trunk/javadoc/com/google/inject/assistedinject/FactoryModuleBuilder.html)
- should the assisted parameters be injectable to fields (and methods when/if we support them) or only as constructor arguments?
Once we settled on the use-cases:
- how do we want to disambiguate arguments of the same type?
To post to this group, send email to dagger-discuss@googlegroups.com.
To unsubscribe from this group, send email to dagger-discuss+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
--
You received this message because you are subscribed to the Google Groups "Dagger Discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to dagger-discuss+unsubscribe@googlegroups.com.
To post to this group, send email to dagger-discuss@googlegroups.com.
To post to this group, send email to dagger-discuss@googlegroups.com.
To unsubscribe from this group, send email to dagger-discuss+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
--
You received this message because you are subscribed to the Google Groups "Dagger Discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to dagger-discuss+unsubscribe@googlegroups.com.
To post to this group, send email to dagger-discuss@googlegroups.com.
What was the outcome? Will Dagger get assisted inject? :)
On 29 Jan 2013, at 5:25, Thomas Broyer wrote: