3

Considering modules are all shared among one-another through the Dagger1 specification of complete=false, library=true, you can receive the elements provided by the @Provides methods through a constructor parameter, like so.

public class GetUserForUsernameTaskImpl
        implements GetUserForUsernameTask {
    public static final String TAG = GetUserForUsernameTaskImpl.class.getSimpleName();

    private Realm realm;
    private UserRepository userRepository;

    public GetUserForUsernameTaskImpl(Realm realm, UserRepository userRepository) {
        this.realm = realm;
        this.userRepository = userRepository;
    }

    @Override
    public RealmResults<UserRLM> getUsers() {
        try {
            RealmResults<UserRLM> users = userRepository.findAll(realm);
    ...
}

@Module(includes = {RepositoryModule.class, RealmModule.class})
public class DatabaseTaskModule {
    @Provides
    public GetUsersDatabaseTask getUsersDatabaseTask(Realm realm, UserRepository userRepository) {
        return new GetUsersDatabaseTaskImpl(realm, userRepository);
    }
}

However, you could also specify only one dependency (the Presenter or the CustomApplication instance) that holds the component graph, and use that component graph to inject your implementation classes.

public class GetUserForUsernameTaskImpl
        implements GetUserForUsernameTask {
    public static final String TAG = GetUserForUsernameTaskImpl.class.getSimpleName();

    @Inject
    public Realm realm;
    @Inject
    public UserRepository userRepository;

    protected Presenter presenter;
    private boolean isInjected = false;

    public GetUserForUsernameTaskImpl(Presenter presenter) {
        this.presenter = presenter;
    }

    @Override
    public RealmResults<UserRLM> getUsers() {
        if(!isInjected) {
            presenter.getPresenterComponent().inject(this);
            isInjected = true;
        }
        try {
            RealmResults<UserRLM> users = userRepository.findAll(realm);
            ...
    }
}

@Module(includes = {PresenterModule.class})
public class DatabaseTaskModule {
    @Provides
    public GetUsersDatabaseTask getUsersDatabaseTask(Presenter presenter) {
        return new GetUsersDatabaseTaskImpl(presenter);
    }
}

And that way, you'd only have to depend on the presenter's object graph, and not have to mess with constructor parameters.

Which one is a better approach?

EDIT: A clearer and concrete example I have in a not-too-well refactored project is the following:

@Module(includes = {ContextModule.class})
public class ClientAuthModule {
    @Provides
    public ClientAuthAuthenticator clientAuthAuthenticator(CustomApplication customApplication) {
        return new ClientAuthAuthenticator(customApplication);
    }
}

Then

public class CustomApplication
        extends Application {
    public static class InjectorInitializedEvent {
    }

    public static class InjectorInitializedEventProducer {
        @Produce
        public InjectorInitializedEvent produceEvent() {
            return new InjectorInitializedEvent();
        }
    }

    private ApplicationComponent applicationComponent;

    @Override
    public void onCreate() {
        super.onCreate();
        applicationComponent = Injector.INSTANCE.initializeApplicationComponent();
        SingletonBus.INSTANCE.getBus().post(new InjectorInitializedEvent());
        SingletonBus.INSTANCE.getBus().register(new InjectorInitializedEventProducer()); //OTTO bus, event producer
    }

    public ApplicationComponent getApplicationComponent() {
        return this.applicationComponent;
    }
}

Then

public class ClientAuthAuthenticator {
    private CustomApplication customApplication;

    @Inject
    public PEMConverter pemConverter;
    @Inject
    public KeyPairCreator keyPairCreator;
    @Inject
    public PKCS10CsrCreator pkcs10CsrCreator;
    @Inject
    public KeyPairReader keyPairReader;
    //...

    public ClientAuthAuthenticator(CustomApplication customApplication) {
        this.customApplication = customApplication;
        SingletonBus.INSTANCE.getBus().register(this);
    }

    @Subscribe
    public void onInjectorInitializedEvent(CustomApplication.InjectorInitializedEvent e) {
        customApplication.getApplicationComponent().inject(this);
        SingletonBus.INSTANCE.getBus().unregister(this);
    }

    ...

The question: This way, all dependencies are provided from the application's component when the injector is ready, rather than through the constructor. But is this a good approach? Are there any caveats on the long-run?

EDIT2: I had the injector here, but it was pretty bad.

The one I have in my other project was better constructed.

Community
  • 1
  • 1
EpicPandaForce
  • 79,669
  • 27
  • 256
  • 428

2 Answers2

1

Using the constructor injection method you are only annotating your classes with the standard JSR-330 annotations, so:

  • The code is decoupled from the concrete dependency injection library you are using. If you need to go back to, let's say, an old version of dagger, only your module and component definitions will have to change, not your core classes.

  • Your classes do not need to be aware of the details of how they are generated. Tomorrow you might have different components according to the scope, or you might want to get your component out of the presenter classes. Your core classes should not change because of this.

  • It makes testing your modules easier. No need to run code generation or replace component instances through your application. Just inject your mock or test modules directly in the constructor and your are done.

Field and method injection should only be used in cases where the lifecycle of a module is out of your control. This would be the case of Activities or Fragments in an Android environment. Outside of that use case, those methods are just a source of problems.

ivagarz
  • 2,434
  • 22
  • 22
-1

If your dependency is provided through a component / module, then the component will always exist when the dependency is created. So you can call field injection in its constructor. It can't be simpler than that.

EpicPandaForce
  • 79,669
  • 27
  • 256
  • 428