2011-07-03 | Filed Under Programming
I find that this comes up fairly frequently. You find some useful library: perhaps it does logging, or enforces design-by-contract, or it provides an API for calling web services. But someone on the team suggests that instead of using the library directly, we should create a wrapper: “that way, if we ever decide to switch to a different library instead it will be easy to switch”. Is this a good idea?
There are a few really good reasons for wrapping a library. The most important of these, is in order to add functionality or simplify use of the library. For instance, in a recent project we used Spring’s library for web service calls in order to make calls to our own company’s collection of web services. But when calling our web services, there are a bunch of things that would be nice to do. We always want the same value for the address to connect to, the timeout for the calls, and the set of headers to provide. We want additional special handling for errors wrapped around every call. Adding these features in a wrapper makes the wrapper less powerful (now it’s good only for calling our services whereas Spring’s original library could call any web service), but at the same time makes it much more useful for that one specific purpose.
I have also seen cases where the existing library had a terrible interface (API), and the wrapper attempts to make it palatable. The “Slick” library is a
Python[ed] Java wrapper around LWJGL adding no real functionality but making it decent enough to use. This is a rare use case: most libraries that have a terrible interface also have lousy features and you’re better off finding a different library instead.
The most common argument that I hear is neither of these cases: the most common argument that I hear is that we should wrap the library so we can easily switch to a different library. In fact, I most often hear this from people developing in a language with strong typing, such as Java. I find this argument completely unpersuasive, for two reasons. First of all, when you switch libraries, the new library typically does not have exactly the same API. For example, when we switched to SLF4J for logging one of the reasons for doing so was that it offered a better API that allowed functionality not possible with the previous API. Secondly, if you DO switch to a library with an API that is equivalent, in a strongly-typed language you can use standard refactoring tools to perform the switch without any risk of introducing bugs. (If the APIs are close enough a simple search-and-replace for an import statement may do it.)
There are advantages to using a library directly. Developers who have encountered the library elsewhere may already be familiar with it. The documentation for the library is likely to be far more extensive than the documentation for your wrapper. It is often safe to assume that the designers of the library are better at designing an API for this feature than you are. As the library is upgraded, newer features will automatically be available. Most of all, having one fewer layers means there is simply less to learn to understand the system.
There are still a few advantages to wrapping without extra features. It gives you a place to add some logging code, or timers around an external call (for profiling), or validation checks. And there are some cases where you want to be able to use different libraries with the same codebase — then a wrapper is indispensable. Commons Logging is an example of this: it allows a library to use different logging frameworks depending on what application it has been embedded in.
So my approach to the “wrap or not to wrap” question goes like this. First of all, will I add functionality with my wrappers or will removing functionality but thereby simplify the interface? If so, then wrapping makes sense. Secondly, if I haven’t yet chosen which library to use or if I want to switch back and forth between libraries, then a wrapper will be required. If neither of these applies, then I begin with a strong presumption that I should use the library on its own, and only a real need to add logging, monitoring, or other wrapped behavior will persuade me otherwise.