Writing Change Friendly CodeTapiwa MuziraBlockedUnblockFollowFollowingApr 8Image by clipartix.
comOne of the most useful and time-saving skills as an Android developer (or in any domain of software engineering) is writing code that is easily adaptable to future changes.
And this skill becomes even more valuable as the size and complexity of the projects you work on increase.
In modern software development you almost always incorporate code, in the form of libraries, that has been created and is maintained by someone which does what you are looking for.
That’s just the beauty of open source, it saves us a lot of time and effort.
However it brings in some uncertainty in the future evolution of our project as over time new backwards-incompatible improved versions of these libraries might be released or better alternatives might come up that make others obsolete.
This makes it vital to structure our code in a way that will make it easy to adapt to any future change.
In this article we will see how we can make our code change-friendly through the technique of data encapsulation and centralized dependency management.
Now let’s see how this can be achieved in a small Android app example.
This is a quite a simple and contrived example but it suffices to explain the basic ideas behind the above mentioned techniques.
This article assumes you are familiar with using the android RecyclerView and the Dagger2 dependency injection framework (links to tutorials at the end of article).
So let’s suppose we are building an app that simply loads images (say movie poster images) from a network resource and displays them in a RecyclerView list.
Now say we have two image loading libraries we can chose from — Glide or Picasso.
Now let’s look at how we might implement this without code agility in mind.
Here is one way we can implement our RecyclerView’s adapter:In the above code, we’ve just simply “welded” in the Glide implementation into our adapter.
Simple, effortless, no pain.
And if we later on decide to change to Picasso we have to go into our adapter and delete all the Glide stuff and implement Picasso so that our onBindView method will look like this:Again simple and painless.
So what’s the fuss?.Well what if your app has got multiple Activities / Fragments with RecylerViews that use different adapter implementations?.For example we can have a MovieDetailActivity that will display details about the selected movie and also a “mini” RecyclerView at the bottom that shows other movies similar to the one displayed.
You will also have to change the adapter in that activity as well to use Picasso.
Now we are starting to do a bit more work to make the change.
So let’s see how we can make our code more agile and change-friendly.
Let’s start by creating our own custom type which we will use to encapsulate the Glide and the Picasso stuff:Then we create an implementation with Glide of our custom type ImageLoader:Now we can prepare our RecylerView adapter to utilize our image loader:Now what we have effectively done above is hide (or encapsulate if you like) from our adapter the details of how images are loaded.
All that our adapter now knows is that it accepts as one of it’s inputs an object of type ImageLoader and can call the loadImage() method on it passing in the required arguments.
Information hiding through encapsulation like this is a powerful concept and we are going to see just why.
We then initialize our RecyclerView adapter in our Main Activity’s onCreate as follows:Now to change from using Glide to Picasso we first create the Picasso implementation of ImageLoader as follows:….
and again in our Main Activity’s onCreate we initialize our adapter as follows:Notice that this time we didn’t touch our RecyclerView adapter to make the necessary changes.
Sweet right?.Not quite because now we are touching our MainActivity instead and we will also have to touch our hypothetical MovieDetailActivity mentioned earlier because it too needs to be changed to utilize our new PicassoImageLoader.
Enter centralized dependency management……Now let’s see how centralized dependency management through a dependency injector helps us to make our code even more change-friendly.
We will be using the Dagger2 dependency injection framework.
Now we are going to skip all the other Dagger2 implementation steps and go right to the point of this article with respect to Dagger2.
So our Dagger2 app module and component using Glide that will provide our GlideImageLoader whenever it’s needed looks like this:And then we implement Dagger in our MainActivity as follows:And Let’s then suppose our other activity (MovieDetailActiviy) looks like this:Now when we decide to change our project to using Picasso we now only need to change one place… yes you guessed it, we only need to refactor the ImageLoader provider method in our dependency module (MyAppModule) to return the Picasso implementation :)And Dagger will hunt down all the ImageLoader dependencies in our project (in this case the @Inject annotated ImageLoader fields in MainActivity and MovieDetailActivity) and inject the provided ImageLoader.
No Activity touched, no adapter touched.
Now that’s sweet and easy!That’s it folks and thanks for reading!.
Resources:That Missing Guide: How to use Dagger2Dagger is a dependency injection framework, which makes it easier to manage the dependencies between the classes in our…medium.