nPn
nPn

Reputation: 16728

How can I add dependency injection after the fact?

I have learned a lot developing my current android app, enough to see that I made some mistakes. For example I now know that all the new Blah(...) statements below are bad from a test point of view. Are there established techniques to safely migrate existing code like this to use a form of dependency injection? By "safe" I mean low likely hood to break by app. I was thinking of just adding setter methods for each dependency so that I could pass in mock objects during test, but that seems a bit hackish.

@Override
public void onCreate() {
    super.onCreate();
    System.out.println("creating the LocationMonitorService");
    mBus = BusProvider.getInstance();
    mBus.register(this);
    markerRequestTimer = new GetMarkerRequestTimer(10*60000,10000);
    pushRequestTimer = new PushRequestTimer(10*60000,60000);
    deviceLocationClient = new DeviceLocationClient(this);
    gcmKeepAliveIntent = new Intent("com.gmail.npnster.first_project.gcmKeepAlive");
    gcmKeepAlivePendingIntent = PendingIntent.getBroadcast(this, 0, gcmKeepAliveIntent, PendingIntent.FLAG_CANCEL_CURRENT);
    alarmManager = (AlarmManager)this.getSystemService(Context.ALARM_SERVICE);
    alarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, 1000, 4*60*1000, gcmKeepAlivePendingIntent);


}

class GetMarkerRequestTimer extends CountDownTimer {

    public GetMarkerRequestTimer(long millisInFuture, long countDownInterval) {
        super(millisInFuture, countDownInterval);
        // TODO Auto-generated constructor stub
    }

    @Override
    public void onTick(long millisUntilFinished) {
        System.out.println("requesting new markers from server");
        mBus.post(new GetMapMarkersRequest());

    }

    @Override
    public void onFinish() {
        System.out.println("time out reached ending request for markers from the server");  


    }

}

Upvotes: 2

Views: 161

Answers (1)

triggerNZ
triggerNZ

Reputation: 4751

Seeing you are on android, I am assuming you use Dagger. If using Guice, replace ObjectGraph with Injector. The rest is the same.

I haven't had to do this refactoring on android before but have done it in server-side java projects. You can incrementally add dependency injection. Starting with:

class A {
  B b = new B();
}

class B {
  C c = new C();
}

class C {
}

Now let's assume we want to refactor B to use DI, but without changing the structure of A.

Create a new ObjectGraph somewhere central. Make it static and public (bad in general, good for incremental transition)

public void onCreate()  {
    ObjectGraph objectGraph = ObjectGraph.create(new MyModule());
    Global.objectGraph = objectGraph; //Where global is just a class with a public static field called objectGraph
    MyApp app = objectGraph.get(App.class);
    ...
  }

So now you can refactor B:

class B {
  private final C c;
  @Inject
  B(C c) {
      this.c = c;
  }
}

B is now injected and testable. To get rid of the new call in A, replace

new B()

with

Global.objectGraph.get(B.class)

Which effectively gives you a fully refactored B and a backwards-compatible A. When, if ever you get rid of all the static references to the global object graph, you can delete it.

Upvotes: 2

Related Questions