1

TL;DR;

Is it acceptable for a class to depend on the ObjectGraph itself?


I need this because I need to inject dependencies on some objects that I load at runtime - at a time that is disconnected from the point at which the ObjectGraph is initialized. Here is an example that illustrates how I use ServiceLoader framework to load concrete implementation classes of a Service at runtime, and then inject dependencies into the loaded implementation classes.

interface Plugin {
    void doSomething();
}

class AwesomePlugin implements plugin {
    @Inject DependencyOne dependencyOne; 
    @Inject DependencyTwo dependencyTwo;
    void doSomething(){
        // ...some implementation...
    }
}

class PluginEngine{
    public void start(){
        ServiceLoader<Plugin> pluginLoader = ServiceLoader.load(Plugin.class);
        for(Plugin plugin: pluginLoader){
            //TODO: Inject plugin dependencies here
        }
    }
}

Doing this would require the PluginEngine class to have access to the ObjectGraph instance:

class PluginEngine{
    private final ObjectGraph objectGraph;
    
    public PluginEngine(ObjectGraph graph){
        this.objectGraph = graph;
    }
    
    public void start(){
        ServiceLoader<Plugin> pluginLoader = ServiceLoader.load(Plugin.class);
        for(Plugin plugin: pluginLoader){
            objectGraph.inject(plugin);
        }
    }
}

Is this a code smell? Is this pointing to some problem elsewhere in my code, or in the way my dependencies are set up?

While composing this question, I began to see the role of Dagger as a means of replacing arbitrary dependencies with a dependency on the ObjectGraph itself. On Android, you use a reference to the custom Application sub-class and use it to perform injection - which is basically just a means to get access to the ObjectGraph itself. Is this reasoning flawed?

Community
  • 1
  • 1
curioustechizen
  • 10,572
  • 10
  • 61
  • 110
  • @EpicPandaForce This is for Dagger 1.x. Edited the title to clarify this. IIRC, Dagger2 does not have the ObjectGraph at all. – curioustechizen Mar 31 '15 at 07:36
  • Ah, okay. I was curious because I haven't used Dagger2 (only Dagger1), but never encountered the `ServiceLoader`. Is that your own code? I don't think you can obtain the `@Inject Objectgraph objectGraph` dependency without... well, injecting it with `objectGraph.inject(this)`, for which you'd need `objectGraph` which you cannot inject without itself, therefore I don't think this will work, unless there's something here that I don't see. – EpicPandaForce Mar 31 '15 at 07:39
  • @EpicPandaForce ServiceLoader is a general Java facility for loading concrete implementation classes at runtime. Here's the link to the docs from Android - but it is available in other Java implementations as well - https://developer.android.com/reference/java/util/ServiceLoader.html. You're right about not being able to inject the ObjectGraph itself. I should probably move it into a constructor argument. – curioustechizen Mar 31 '15 at 07:44
  • Ah. I'll read about ServiceLoaders then. I personally used the following setup, snatched the structure from the code generated by `Android Bootstrap`: http://stackoverflow.com/a/27036934/2413303 – EpicPandaForce Mar 31 '15 at 08:10

1 Answers1

1

To answer my own question, it looks like this is acceptable. The u2020 sample app does something roughly similar. In fact it makes some very clever use of getSystemService() to achieve this but that is Android specific. In particular, look at Injector.java and how it is used from within custom views like TrendingView

So, conceptually, one could do something like this - which basically abstracts the concrete ObjectGraph dependency behind an Injector interface.

class PluginEngine{
    private final Injector injector;

    public PluginEngine(Injector injector){
        this.injector = injector;
    }

    public void start(){
        ServiceLoader<Plugin> pluginLoader = ServiceLoader.load(Plugin.class);
        for(Plugin plugin: pluginLoader){
            injector.inject(plugin);
        }
    }
}

This can be refined/adjusted in various ways such that the injector dependency is provided via a constructor or obtained in other ways.

curioustechizen
  • 10,572
  • 10
  • 61
  • 110