78

Most projects I am involved with use several open-source components. As a general principle, is it a good idea always to avoid binding all components of the code to the third-party libraries and instead go via an encapsulating wrapper to avoid the pain of change?

As an example, most of our PHP projects directly use log4php as a logging framework, i.e. they instantiate via \Logger::getLogger(), they use ->info() or ->warn() methods, etc. In the future, however, a hypothetical logging framework may appear which is better in some way. As it stands, all the projects which closely couple to the log4php method signatures would have to change, in dozens of places, in order to fit the new signatures. This would obviously have a wide impact on the codebase and any change is a potential problem.

To future-proof new codebases from this kind of scenario, I often consider (and sometimes implement) a wrapper class to encapsulate the logging functionality and make it easier, though not foolproof, to alter the way in which logging works in future with minimal change; the code calls the wrapper, the wrapper passes the call to the logging framework du jour.

Bearing in mind that there are more complicated examples with other libraries, am I over-engineering or is this a wise precaution in most cases?

EDIT: More considerations - using dependency injection and test doubles practically requires that we abstract out most APIs anyway ("I want to check my code executes and updates its state, but not write a log comment/access a real database"). Isn't this a decider?

  • 3
    log4XYZ is such a strong trademark. Its API will change not sooner than when the API for a linked list will. Both are a long solved problem now. – Job Sep 11 '11 at 15:20
  • 1
    Exact duplicate of this SO question: stackoverflow.com/questions/1916030/… – Michael Borgwardt Sep 11 '11 at 15:26
  • 1
    If you're just using it internally, whether you wrap or not is just a trade-off between known work now and possible work later. A judgement call. But something other responders seem to have neglected to talk about is whether it's an API dependency or an implementation dependency. In other words, are you leaking classes from this third party API through your own public API, and exposing it to users? In this case it's no longer a simple matter of hard work to move to a different library, the problem is that it's now impossible without breaking your own API. This is very bad! – Elias Vasylenko Jun 26 '17 at 21:34
  • 1
    For further reference: This pattern is called onion-architecture where external infrastructure (you call it external lib) is hidden behind an interface – k3b Aug 1 '17 at 12:53

10 Answers 10

42

If you only use a small subset of the third party API, it makes sense to write a wrapper - this helps with encapsulation and information hiding, ensuring you don't expose a possibly huge API to your own code. It can also help with making sure that any functionality you don't want to use is "hidden".

Another good reason for a wrapper is if you expect to change the third party library. If this is a piece of infrastructure you know you will not change, do not write a wrapper for it.

  • Good points, but we are taught that closely-coupled code is bad, for many well-understood reasons (harder to test, harder to refactor, etc). An alternative wording of the question is "if coupling is bad, why is it OK to couple to an API?". – lotsoffreetime Sep 11 '11 at 8:53
  • 7
    @lotsoffreetime You can't avoid some coupling to an API. Therefore, it's better to couple to your own API. That way, you can change out the library and generally not need to change the API provided by the wrapper. – George Marian Sep 11 '11 at 9:06
  • @george-marian If I can't avoid using a given API, I can certainly minimise the touch points. The question is, should I be trying to do this all the time or is that overdoing things? – lotsoffreetime Sep 11 '11 at 9:34
  • 2
    @lotsoffreetime That is a difficult question to answer. I've expanded on my answer to that end. (Basically, it's down to a lot of ifs.) – George Marian Sep 11 '11 at 9:49
  • 2
    @lotsoffreetime: if you have lots of free time, then you can do either. But I'd recommend against writing an API wrapper, except under this condition: 1) the original API is very low level, so you write a higher level API to suit your specific project need better, or 2) you have a plan in the near future to switch libraries, you're using the current library only as a stepping stone while searching for a better one. – Lie Ryan Sep 11 '11 at 9:57
28

Without knowing what super-great new features this alleged future improved logger will have, how would you write the wrapper? The most logical choice is to have your wrapper instantiate some sort of logger class, and have methods like ->info() or ->warn(). In other words, essentially identical to your present API.

Rather than future-proof code that I may never need to change, or that may require an unavoidable rewrite anyway, I prefer to "past-proof" code. That is, on the rare occasions when I do significantly change a component, that's when I write a wrapper to make it compatible with past code. However, any new code uses the new API, and I refactor old code to use it whenever I'm making a change in the same file anyway, or as schedule permits. After a few months, I can remove the wrapper, and the change has been gradual and robust.

Put another way, wrappers really only make sense when you already know all the APIs you need to wrap. Good examples are if your application currently needs to support many different database drivers, operating systems, or PHP versions.

  • "...wrappers really only make sense when you already know all the APIs you need to wrap." This would be true if I were matching the API in the wrapper; perhaps I should be using the term "encapsulation" more strongly than wrapper. I would be abstracting these API calls to "log this text somehow" rather than "call foo::log() with this parameter". – lotsoffreetime Sep 11 '11 at 8:34
  • "Without knowing what super-great new features this alleged future improved logger will have, how would you write the wrapper?" @kevin-cline below mentions a future logger with better performance, rather than a newer feature. In this case, no new API to wrap, just a different factory method. – lotsoffreetime Sep 11 '11 at 8:35
27

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.

  • 1
    This applies just as well to an unstable API as well. Our code doesn't change in 1000 places just because the underlying library changed. Very nice answer. – RubberDuck Sep 25 '15 at 2:56
  • Very concise and clear answer. I do front-end work on the web. The amount of changes in that landscape is insane. The fact that people "think" their will be no changes doesn't mean there won't be any changes. I saw mentions of YAGNI. I would like to add a new acronym, YDKYAGNI, You dont know you aint gonna need it. Especially with web related implementations. As a rule I always wrap libraries that only expose a small API (like select2). Larger libraries affect your architecture and wrapping them means you are expecting your architecture to change, it might, but it's less likely to do so. – Byebye Dec 17 '16 at 10:08
  • Your answer was super helpful and presenting the concept with an example made the concept even more clear. – Anil Gorthy Feb 2 '18 at 19:26
24

I think that wrapping third-party libraries today in case something better comes along tomorrow is a very wasteful violation of YAGNI. If you are repeatedly calling third-party code in a manner peculiar to your application, you will (should) refactor those calls into a wrapping class to eliminate the repetition. Otherwise you are fully using the library API and any wrapper would look just like the library itself.

Now suppose a new library appears with superior performance or whatever. In the first case, you just rewrite the wrapper for the new API. No problem.

In the second case, you create a wrapper adapting the old interface to drive the new library. A little more work, but no problem, and no more work than you would have done if you had written the wrapper earlier.

  • 4
    I don't think that YAGNI necessarily applies in this situation. It isn't about building in functionality in case you may need it in the future. It is about building flexibility into the architecture. If that flexibility is unnecessary, then, yes, YAGNI applies. However, that determination tends to be made sometime in the future when making the change will likely be painful. – George Marian Sep 11 '11 at 9:29
  • 7
    @George Marian: the problem is 95% of the time, you will never need the flexibility to change. If you need to switch to a future new library that has superior performance, then it should be fairly trivial to search/replace calls or write a wrapper when you need it. On the other hand, if your new library comes with different functionalities, the wrapper now becomes a hindrance since now you have two problems: porting old code to exploit the new features and maintaining the wrapper. – Lie Ryan Sep 11 '11 at 10:09
  • 3
    @lotsoffreetime: The purpose of "good design" is to minimize the total cost of the application over it's life. Adding layers of indirection for imagined future changes is very expensive insurance. I have never seen anyone realize any savings from that approach. It just creates trivial work for programmers whose time would be much better spent on customer-specific requirements. Most of the time, if you are writing code that is not specific to your customer, you are wasting time and money. – kevin cline Sep 11 '11 at 14:14
  • 1
    @George: if these changes are painful, I think that is a process smell. In Java, I would create new classes with the same names as the old classes, but in a different package, change all occurrences of the old package name, and rerun the automated tests. – kevin cline Sep 11 '11 at 17:08
  • 1
    @kevin That is more work, and thus carries more risk, than simply updating the wrapper and running the tests. – George Marian Sep 12 '11 at 6:50
9

The basic reason to write a wrapper around a third-party library is so that you can exchange that third-party library without changing the code that uses it. You can't avoid coupling to something, so the argument goes that it is better to couple to an API you've written.

Whether this is worth the effort is a different story. That debate will likely continue for a long time.

For small projects, where the likelihood that such a change will be necessary is low, it is probably unnecessary effort. For larger projects, that flexibility may very well outweigh the extra effort to wrap the library. However, it is difficult to know whether that is the case beforehand.

Another way to look at it is that basic principle of abstracting what is likely to change. So, if the third-party library is well established and unlikely to be changed, it may be fine not to wrap it. However, if the third-party library is relatively new there is a greater chance that it will need to be replaced. That said, development of established libraries has been abandoned plenty of times. So, this is not an easy question to answer.

  • In the case of unit testing where being able to inject a mock of the API serves to minimise the unit under test, "change potential" isn't a factor. Having said that, this is still my favourite answer as it is closest to how I think. What would Uncle Bob say? :) – lotsoffreetime Sep 11 '11 at 11:26
  • Also, small projects (no team, basic spec, etc) have their own rules in which you can violate good practice such as this and get away with it, to a degree. But that's a different question... – lotsoffreetime Sep 11 '11 at 11:27
1

In addition to what @Oded already said, I'd just like to add this answer for the special purpose of logging.


I always have an interface for logging but I never had to substitute a log4foo framework yet.

It takes only half an hour to provide the interface and write the wrapper, so I guess you don't waste too much time if it turns out to be unecessary.

It's a special case of YAGNI. Although I don't need it it doesn't take much time and I feel safer with it. If the day of exchanging the logger really comes, I'll be glad I invested half an hour because it'll save me more than a day exchanging calls in a real world project. And I've never written or seen a unit test for logging (apart from tests for the logger implementation itself), so expect defects without the wrapper.

  • I don't expect to change log4foo, but it's widely known and serves as an example. It is also interesting how the two answers so far are complementary - "don't always wrap"; "wrap just in case". – lotsoffreetime Sep 10 '11 at 21:34
  • @Falcon: do you wrap everything? ORM, log interface, core language classes? After all, one never can tell when a better HashMap may be needed. – kevin cline Sep 12 '11 at 23:50
1

I'm dealing with this exact issue on a project I'm currently working on. But in my case the library is for graphics and thus I am able to restrict it's use to a small number of classes that deal with graphics, versus sprinkling it throughout the entire project. Thus it's pretty easy to switch APIs later if I need to; in the case of a logger the matter becomes a lot more complicated.

Thus I would say the decision has a lot to do with what exactly the 3rd-party library is doing and how much pain would be associated with changing it. If changing all the API calls would be easy regardless then it's probably not worth doing. If however changing the library later would be really hard then I would probably wrap it now.


Beyond that, other answers have covered the main question very well so I just want to focus on that last addition, about dependency injection and mock objects. It depends of course on how exactly your logging framework works, but in most cases no that wouldn't require a wrapper (although it will probably benefit from one). Just make the API for your mock object exactly the same as the 3rd-party library and then you can easily swap in the mock object for testing.

The main factor here is whether or not the 3rd-party library is even implemented through dependency injection (or a service locator or some such loosely coupled pattern). If the library functions are accessed through a singleton or static methods or something then you will need to wrap that in an object that you can work with in dependency injection.

1

I'm strongly in the wrapping camp and not with being able to substitute the third party library with the biggest priority (though that is a bonus). My main rationale that favors wrapping is simple

Third party libraries are not designed for our specific needs.

And this manifests itself, typically, in the form of a boatload of code duplication, like developers writing 8 lines of code just to create a QButton and style it the way it should look for the application, only for the designer to want not only the look but also the functionality of buttons to change completely for the entire software which ends up requiring going back and rewriting thousands of lines of code, or find that modernizing a rendering pipeline requires an epic rewrite because the codebase sprinkled low-level ad-hoc fixed-pipeline OpenGL code all over the place instead of centralizing a real-time renderer design and leaving the use of OGL strictly for its implementation.

These designs are not tailored to our specific design needs. They tend to offer a massive superset of what's actually needed (and what's not part of a design is as important, if not more, than what is), and their interfaces are not designed to specifically service our needs in a high-level "one thought = one request" sort of way which deprives us of all central design control if we use them directly. If the developers end up writing much lower-level code than should be required to express what they need, they can sometimes end up wrapping it themselves in ad-hoc ways that makes it so you end up with dozens of hastily-written and crudely-designed and documented wrappers instead of one well-designed, well-documented one.

Of course I would apply strong exceptions to libraries where the wrappers are almost one-to-one translations of what the third party APIs have to offer. In that case there might be no higher-level design to be sought out which more directly expresses the business and design requirements (such might be the case for something resembling more of a "utility" library). But if there is a much more tailored design available which much more directly expresses our needs, then I'm strongly in the wrapping camp, just as I'm strongly in favor of using a higher-level function and reusing it over inlining assembly code all over the place.

Oddly I've clashed with developers in ways where they seemed so distrustful and so pessimistic about our ability to design, say, a function to create a button and return it that they'd rather write 8 lines of lower-level code focused on microscopic details of button creation (which ended up needing to change repeatedly in the future) over designing and using said function. I don't even see the purpose of us trying to design anything in the first place if we can't trust ourselves to design these sorts of wrappers in a reasonable way.

Put another way I see third party libraries as ways to potentially save enormous time in implementation, not as substitutes for designing systems.

0

My idea on third-Party Libraries:

There’s been some discussion recently in the iOS community about pros and cons (OK, mostly cons) of using third-party dependencies. Many arguments I saw were rather generic — grouping all third-party libraries into one basket. As with most things, though, it’s not that simple. So, let’s try to focus on a single case

Should we avoid using third-party UI libraries?

Reasons to consider third-party libraries :

There seem to be two main reasons developers consider using a third party library:

  1. Lack of skill or knowledge. Let’s say, you’re working on a photo sharing app. You don’t start by rolling your own crypto.
  2. Lack of time or interest to build something. Unless you have an unlimited amount of time (which no human has yet) you have to prioritize.

Most UI libraries (not all!) tend to fall into the second category. This stuff is no rocket science, but it takes time to build it right.

If it’s a core business function — do it yourself, no matter what it is.

There are pretty much two types of controls/views:

  1. Generic, allowing you to use them in many different contexts not even thought of by their creators, e.g. UICollectionView from UIKit.
  2. Specific, designed for a single use-case, e.g. UIPickerView. Most third-party libraries tend to fall into the second category. What’s more, they’re often extracted from an existing codebase for which they were optimized.

Unknown early assumptions

Many of developers do code reviews of their internal code but may be taking third-party source code’s quality for granted. It’s worth spending a bit of time just browsing a library’s code. You may end up being surprised to see some red flags, e.g. swizzling used where it’s not needed.

Often learning the idea is more beneficial than obtaining the resulting code itself.

You Can’t hide it

Because of the way UIKit is designed you most probably won’t be able to hide the third-party UI library, e.g. behind an adapter. A library will intertwine with your UI code becoming a de-facto of your project.

Future time cost

UIKit changes with each iOS release. Things will break. Your third-party dependency won’t be as maintenance-free as you may expect.

Conclusion :

From my personal experience, most uses of third-party UI code boil down to exchanging smaller flexibility for some time gain.

We use ready-made code to ship our current release faster. Sooner or later, though, we hit the limits of the library and stand before a hard decision: what to do next?

0

Using the library directly is more friendly for the developer team. When a new developer joins, he may be fully experienced with all frameworks used yet will not be able to contribute productively before learning your home grown API. When a younger developer attempts to progress in your group, he would be forced to learn your specific API not present anywhere else, instead of acquiring more useful generic competence. If somebody knows useful features or possibilities of the original API, may not be able to reach over the layer written by someone who was not aware of them. If someone will get a programming task while looking for a job, may not be able to demonstrate the basic things he used many times, just because all these times he was accessing the needed functionality through your wrapper.

I think these issues may be more important than rather remote possibility of using a completely different library later. The only case I would use a wrapper is when migration to another implementation is definitely planned or the wrapped API is not frozen enough and keeps changing.

protected by gnat Jun 6 '17 at 10:31

Thank you for your interest in this question. Because it has attracted low-quality or spam answers that had to be removed, posting an answer now requires 10 reputation on this site (the association bonus does not count).

Would you like to answer one of these unanswered questions instead?

Not the answer you're looking for? Browse other questions tagged or ask your own question.