Introduction
I guess that you are in a similar situation as me, that most projects that you are working on are dealing with third-party libraries. There is no need to reinvent the wheel since external libraries are usually well tested and documented solutions made by other developers to make your life easier. You should use them, since there is almost nothing wrong with using third party libraries because they can make our development much faster and save us a lot of time.
What is the problem?
The main problem is that you become tightly coupled to that external library and that is something that we should try to avoid as much as possible. You will end up with multiple calls to the external library from your concrete code and as a result, you can not easily replace your 3rd party library, because you will need to find every call to the external library and change its implementation.
Solution
Solution to this problem is using Facade design pattern. Facade hides the complexities of a system and provides an interface to a client from where a client can access a system. It is structural design pattern which main goal is to hide complexity of a system from a client. If you are not familiar with Facade design pattern you can read article from the link below:
By making facade around a third party library you are adding an additional layer of abstraction on top of it. Some advantages are:
- Your code is easier to change
- Easier testing
- Multiple concrete implementations of external libraries
- Low coupling
In general, your goal is to have a system that is easier to change, so you can change your a third-party library with another one.
Example
Nobody likes theory, so I will show you concrete real-world example with an Android application that needs to load images from the Internet. If you are working with Android you probably know that there is a lot of image loading libraries, since working with images on Android is very hard and time-consuming.
One of the most popular solutions was Universal Image Loader library, but the main developer announced that he does not have time for development. So that could be a problem for us, since the concrete code of this library is across all of our codebases and we should found every ImageView component and change code that loads image inside it.
Let’s see how to write code with Facade design pattern that is easy to change, so we can easily remove Universal Image Loader with Picasso image loading library.
Step 1
First we need to set up our project by creating a project and add INTERNET and WRITE_EXTERNAL_STORAGE permission inside Android Manifest file.
<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
Also we need to add Gradle dependencies inside module build.gradle file:
compile 'com.squareup.picasso:picasso:2.5.2' compile 'com.nostra13.universalimageloader:universal-image-loader:1.9.5'
Step 2
We need to define an interface, which will use to wrap image loading libraries.
public interface ImageLibrary { void load(String url, ImageView imageView); }
Step 3
Since in this example we initially used Universal Image Loading library, let’s first write the implementation for that library. We will create UniversalImageLibrary class that will implement ImageLibrary interface.
public class UniversalImageLibrary implements ImageLibrary { private final ImageLoader imageLoader; public UniversalImageLibrary(Context context){ // Create global configuration and initialize ImageLoader with this config ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(context).build(); imageLoader = ImageLoader.getInstance(); imageLoader.init(config); } @Override public void load(String url, final ImageView imageView) { imageLoader.loadImage(url, new ImageLoadingListener() { @Override public void onLoadingStarted(String imageUri, View view) { } @Override public void onLoadingFailed(String imageUri, View view, FailReason failReason) { } @Override public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) { imageView.setImageBitmap(loadedImage); } @Override public void onLoadingCancelled(String imageUri, View view) { } }); } }
Step 4
Now let’s create a class for Picasso image loading library.
public class PicassoImageLibrary implements ImageLibrary { private Context context; public PicassoImageLibrary(Context context){ this.context = context; } @Override public void load(String url, ImageView imageView) { Picasso.with(context).load(url).into(imageView); } }
Step 5
Let’s create a class that will serve as a facade and hides concrete implementations. The first version is using the Universal Image Loader library as concrete implementation. If you are already familiar with a Facade design pattern, then you can easily remove Facade from the class name, since you know class intent and purpose.
public class ImageLoaderFacade { private Context context; public ImageLoaderFacade(Context context){ this.context = context; } public void load(String url, ImageView imageView){ UniversalImageLibrary universalImageLibrary = new UniversalImageLibrary(context); universalImageLibrary.load(url, imageView); } }
Universal Image Loader library is not active, so let’s change our implementation to Picasso. We need just a change the load method to call the class that has Picasso concrete implementation.
public class ImageLoaderFacade { private Context context; public ImageLoaderFacade(Context context){ this.context = context; } public void load(String url, ImageView imageView){ PicassoImageLibrary picasso = new PicassoImageLibrary(context); picasso.load(url, imageView); } }
Step 6
It is time to write some code inside our Activity class. We need to create a facade instance and pass image URL and concrete image view.
public class MainActivity extends AppCompatActivity { private String IMAGE_URL = "http://photos.wikimapia.org/p/00/03/04/64/07_big.jpg"; private ImageView imageView; private ImageLoaderFacade imageLoaderFacade; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); imageView = (ImageView) findViewById(R.id.imageView); imageLoaderFacade = new ImageLoaderFacade(this); imageLoaderFacade.load(IMAGE_URL, imageView); } }
Conclusion
As you can see, we have easily changed from Universal Image Loader to Picasso, since we wrote a concrete implementation of Picasso library and inside our Facade class just changed call to Picasso class. Without this approach, we would end hunting all image loading calls and changing the concrete implementation of the Universal Image Loader library to Picasso.
You should use external libraries since they will speed up your development and you will have more time to solve your business problems. This does not mean that you must make facade around every external library, but the general idea is that you do not want to be dependent on them. Use Facade design pattern when you are expecting to change the external library, the library is not trusted or you just want to use just a small subset of the library without exposing the whole library to the client.
If you are interested in topics like this about development, you can follow me on Twitter @Zookey90.
No Comments