loading...

Making your code more flexible : Before Dagger, Part 0

aniketsmk profile image Aniket Kadam Updated on ・2 min read

Let's say you have a class that handles everything to do with your notes.
Saving them, loading them up from the database etc.
What if we wanted to end up with an implementation that does this normally, but we can change the database it uses anytime without ever having to change how the NotesHandler works?
Or pass in data for tests without having to create a whole real db for it?
Here's the journey that will get you there.

To start with our class might look like this:

class NotesHandler {

    RealDb db = new RealDb();
    public void saveNote(Note note) {
        db.save(note);
    }
}

The NotesHandler class clearly depends on a database to do some of the things it does.
However, creating a new instance of it inside the class strongly ties the NotesHandler to the RealDb. This means if we wanted to change the db, we have to change a lot more code.

We could rewrite it, passing the db from the outside so at least initialization is done externally:

class NotesHandler{
    private RealDb db;

    NotesHandler(RealDb db) {
        this.db = db;
    }

    public void saveNote(Note note) {
        db.save(note);
    }
}

But, it's not great. If we wanted to pass in some test values, we're now automatically involving the RealDb. Which could time to run and create all kinds of unexpected effects.

But if you think about what we really want, we just want a way to save our notes.
We could express what we want in an interface:

interface DbSource{
    void save(Note note);
}

Which is just a way of saying, we're going to take a class, which absolutely has a save method that takes a note, but we don't care about anything else about the class.

So the NotesHandler can become:

class NotesHandler{
    private DbSource db;

    NotesHandler(DbSource db) {
        this.db = db;
    }

    public void saveNote(Note note) {
        db.save(note);
    }
}

And then wrap our db in a class that extends this interface to provide the save functionality.

class AndroidDbSource implements DbSource {
    RealDb db = new RealDb();

    @Override
    public void saveNote(Note note) {
        db.save(note);
    }
}

And now, we gain an ability.
If we wanted to test this class, we could pass in a FakeDb. Which can look like this.

class FakeDb implements DbSource {
        ArrayList<Note> db = new ArrayList<Note>();

    @Override
    public void save(Note note) {
        db.add(note);
   }
}

Testing

So now you can make NotesHandler any of three ways:

NotesHandler notesHandler = new NotesHandler(new FakeDb());
NotesHandler notesHandler = new NotesHandler(new RealDb());
NotesHandler notesHandler = new NotesHandler(new ReplacementDb());

And now NotesHandler doesn't have to change but its innermost workings kinda do!


This was a prelude to the dagger articles and I'm always happy to get feedback if there was something that wasn't clear so I can explain better!

Discussion

pic
Editor guide