By wrapping a third party library you add an additional layer of abstraction on top of it. This has a few advantages:
Your code base becomes more flexible to changes
If you ever need to replace the library with another one you only need to change your implementation in your wrapper - in one place. You can change the implementation of the wrapper and don't have to change a thing about anything else, in other words you have a loosely coupled system. Otherwise you would have to go through your whole codebase and make modifications everywhere - which is obviously not what you want.
You can define the API of the wrapper independently of the API of the library
Different libraries can have vastly different APIs and at the same time none of them may be exactly what you need. What if some library needs a token to be passed along with every call? You can pass the token around in your app wherever you need to use the library or you can safe it somewhere more centrally, but in any case you need the token. Your wrapper class makes this whole thing simple again - because you can just keep the token inside your wrapper class, never exposing it to any component inside your app and completely abstract away the need for it. A huge advantage if you ever used a library which does not emphasise good API design.
Unit testing is way simpler
Unit tests should only test one thing. If you want to unit test a class you have to mock its dependencies. This becomes even more important if that class makes network calls or accesses some other resource outside of your software. By wrapping the third party library it is easy to mock those calls and return test data or whatever that unit test requires. If you don't have such a layer of abstraction it becomes much more difficult to do this - and most of the time this results in a lot of ugly code.
You create a loosely coupled system
Changes to your wrapper have no effect on other parts of your software - at least as long as you don't change the behaviour of your wrapper. By introducing a layer of abstraction like this wrapper you can simplify calls to the library and and almost completely remove the dependency of your app on that library. Your software will just use the wrapper and it won't make a difference how the wrapper is implemented or how it does what it does.
Practical Example
Let's be honest. People can argue about the advantages and disadvantages of something like this for hours - which is why I much rather just show you an example.
Let's say you have some kind of Android app and you need to download images. There are a bunch of libraries out there which make loading and caching images a breeze for example Picasso or the Universal Image Loader.
We can now define an interface which we are going to use to wrap whichever library we end up using:
public interface ImageService {
Bitmap load(String url);
}
This is the interface we can now use throughout the app whenever we need to load an image. We can create an implementation of this interface and use dependency injection to inject an instance of that implementation everywhere we use the ImageService
.
Let's say we initially decide to use Picasso. We can now write an implementation for ImageService
which uses Picasso internally:
public class PicassoImageService implements ImageService {
private final Context mContext;
public PicassoImageService(Context context) {
mContext = context;
}
@Override
public Bitmap load(String url) {
return Picasso.with(mContext).load(url).get();
}
}
Pretty straight forward if you ask me. Wrapper around libraries don't have to be complicated to be useful. The interface and the implementation have less than 25 combined lines of code so it was barely any effort to create this, but already we gain something by doing this. See the Context
field in the implementation? The dependency injection framework of your choice will already take care of injecting that dependency before we ever use our ImageService
, your app now does not have to care about how the images are downloaded and whatever dependencies that library may have. All your app sees is an ImageService
and when it needs an image it calls load()
with with an url - simple & straightforward.
However the real benefit comes when we start to change things. Imagine we now need to replace Picasso with Universal Image Loader because Picasso does not support some feature we absolutely need right now. Do we now have to comb through our codebase and and tediously replace all the calls to Picasso and then deal with dozens of compile errors because we forgot to a few Picasso calls? No. All we need to do is create a new implementation of ImageService
and tell our dependency injection framework to use this implementation from now on:
public class UniversalImageLoaderImageService implements ImageService {
private final ImageLoader mImageLoader;
public UniversalImageLoaderImageService(Context context) {
DisplayImageOptions defaultOptions = new DisplayImageOptions.Builder()
.cacheInMemory(true)
.cacheOnDisk(true)
.build();
ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(context)
.defaultDisplayImageOptions(defaultOptions)
.build();
mImageLoader = ImageLoader.getInstance();
mImageLoader.init(config);
}
@Override
public Bitmap load(String url) {
return mImageLoader.loadImageSync(url);
}
}
As you can see the implementation might be very different, but it does not matter. We didn't have to change a single line of code anywhere else in our app. We use a completely different library which might have completely different features or might be used very differently but our app just does not care. Same as before the rest of our app just sees the ImageService
interface with its load()
method and however this method is implemented does not matter any more.
At least to me this all already sound pretty nice already, but wait! There's still more. Imagine you are writing unit tests for a class you are working on and this class uses the ImageService
. Of course you can't let your unit tests make network calls to some resource located on some other server but since you are now using the ImageService
you can easily let load()
return a static Bitmap
used for the unit tests by implementing a mocked ImageService
:
public class MockImageService implements ImageService {
private final Bitmap mMockBitmap;
public MockImageService(Bitmap mockBitmap) {
mMockBitmap = mockBitmap;
}
@Override
public Bitmap load(String url) {
return mMockBitmap;
}
}
To summarize by wrapping third party libraries your code base becomes more flexible to changes, overall simpler, easier to test and you reduce the coupling of different components in your software - all things that get more and more important the longer you maintain a software.