How do I inject an abstraction using an IOC container like Dagger 2

531 views Asked by At

What I would like to do is to have an abstraction, and pass the abstraction as parameter. So that if one day something better than retrofit comes along and I want to switch it, it will be easy to do so. Although I have no idea on how do it. Here is a basic overview of what I want to do:

public interface ApiClient{
    Object getClient()
}

public class RetrofitClient implements ApiClient{
    private static Retrofit retrofit = null;

    @Override
    public Retrofit getClient(){
        if(retrofit == null){
            OkHttpClient tokenInterceptor = new OkHttpClient.Builder()
                  .addInterceptor(new NetworkInterceptor())
                  .build();

            retrofit = new Retrofit.Builder()
                  .baseUrl("www.hello.com")
                  .addConverterFactory(GsonConverterFactory.create())
                  .build();
        }
    }
}

And then I would like to consume it by passing ApiClient as parameter:

public class MyClass{
    private ApiClient apiClient;

    public MyClass(ApiClient apiClient){
        this.apiClient =  apiClient;
    }

    public void getSomeData(){
        MyClient client = this.client.create(MyClient.class);
    }
}

I would need some way of injecting RetrofitClient into MyClass. Please suggest an IOC container any other alternative and how I would use it to achieve this, thanks.

This link provides a more in depth description on it is done using C# and Castle Windsor https://github.com/castleproject/Windsor/blob/master/docs/basic-tutorial.md

1

There are 1 answers

0
David Rawson On BEST ANSWER

In Java, as in C#, abstractions are represented by interfaces or abstract classes.

What you are proposing might be called 'wrapping an external library in an interface to allow swapping by configuration'. To do that, you would have to inspect the methods exposed by the concretion that you want to be able to swap out and create a new interface that matches those.

For example, if the external library has methods like this:

public class ExternalLibrary {
    void foo() {
        //foo implementation
    }

    void bar() {
        //bar implementation
    }
}

You would create a wrapper interface:

public interface FooBarable {
    void foo();
    void bar();
}

The next step is to make the consumers of ExternalLibrary in your app depend on FooBarable:

public class MyActivity extends Activity {

    FooBar fooBar;

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        DaggerFooBarComponent
            .builder()
            .fooBarModule(new ExternalLibraryModule())
            .build()
            .inject(this);
    }
}

If you need to swap ExternalLibrary for, say, a test mock, you can use a test component as per the instructions for testing in the Dagger 2 User Guide:

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        DaggerTestFooBarComponent
            .builder()
            .mockFooBarModule(new MockFooBarableModule())
            .build()
            .inject(this);
    }

To see how the DI framework/IOC container Dagger 2 works with all of this, you would have to look at the user guide and the Google Dagger 2 Android app blueprint as there is too much to explain within the context of one answer.