The adapter structural pattern allows you to change the interface of an object. This lets you reuse code that would not normally fit into your design.
This is a simple pattern that mostly describes how to forward calls from a desired interface method to another method that you’re trying to adapt. You can either use inheritance or object composition to accomplish this.
Your first choice is to use inheritance. Create a class that inherits from an interface your code expects and then also inherits from the other code that you’re trying to adapt. You’ll normally inherit publicly from the interface that your code expects and privately from the class you’re adapting.
Your other choice is to use object composition where your wrapper just implements the interface your code expects and forwards any necessary calls to a member variable of the class you’re adapting. This will normally be a pointer because that gives you the power to forward calls not just to some predefined class but to any other class that inherits from the class you’re adapting.
Many times, this design pattern is called a wrapper. Listen to the full episode or you can also read the full transcript below.
The basic description says that this pattern lets classes work together that would not otherwise be able to.
This design pattern has so many similarities to everyday examples that I’m not sure where to begin. I’ll give you several examples and hope that at least one will be something that you can relate to.
The first example is overseas travel plugs or power adapters. These devices are designed to allow you to plug your electrical devices into the adapter with one set of prong arrangement and then plug into the wall outlets in a different country. Some travel plugs provide multiple adapters for various countries. Why are there so many standards for plugging things into electricity?
Another example should be familiar to anybody who works with tools. Specifically ratchet sets come in sizes like 3/8 or 1/2 inch. I’m not talking about the size of the bolt that you’re trying to tighten. This is the size of the ratchet tool itself. If you want to use a socket designed to fit a 3/8 inch ratchet but you only have a 1/2 inch ratchet available, then you need a special adapter that accepts the 3/8 inch socket and then snaps into the 1/2 inch ratchet.
And for another example, I’m going old school and talking about record players. Many years ago, you could buy songs on vinyl records and there were two popular formats. The smaller records usually had a single song on each side and played at a faster 45 RPMs. The larger LP records played at a slower 33 and a third RPMs. I always wondered who came up with these speeds. Anyway, the big problem was the center holes in the records were different sizes. A lot different. Not only did your record player need to change the speed that it turned, but you also needed to put an adapter in the larger center hole so the record would fit.
I hope you’re getting the idea that these design patterns are really just a documented and agreed on way to describe concepts that software developers have been using for a long time. Without design patterns, developers would describe their designs with different terminology and then would spend time explaining what they meant.
With design patterns, once you learn them, you’ll be able to explain where in the code you decided to use an adapter and other developers will instantly know exactly what you mean.
And the opposite scenario is something you should avoid. You don’t want another developer to casually mention a design pattern when you don’t know the meaning. That one oversight could cause you to become lost because the other developer will naturally assume you know all about design patterns.
Now that you know how the adapter pattern relates to the real world, I’ll explain some options you have right after this message from our sponsor.
( Message from Sponsor )
This is one of the more simple patterns but even so, you have some options available for you to choose exactly how you’ll implement an adapter.
Remember that an adapter allows you to make use of a class that has a different interface than what your other code expects. Let’s say you need your application to play sounds and you found a great sound library with an excellent set of classes. There’s just one problem. This library uses error codes for all the methods to inform callers when something goes wrong. And your code is written to use exceptions. You could try remembering to check for error codes in your code whenever you use this sound library. But will you handle them all? What about a year from now when other developers have started writing code. Will all future developers show your same attention to detail?
This would be a good time to build a set of classes that adapt the error handling design of the sound library to your app’s error handling. This is a good idea because you can do this work once, make sure it’s correct and complete, and then sit back and catch those exceptions. It’s easy to test such an adapter because your new classes don’t really add anything new. They just wrap up the sound library classes. In fact, this design pattern is sometimes referred to as a wrapper.
But how do you build the wrapper classes? You’ve got two fundamental choices.
Your first choice is to use inheritance. Create a class that inherits from an interface your code expects and then also inherits from the other code that you’re trying to adapt. In this case, you implement the desired methods to just call the sound library methods and check for an error code. It the call failed, then the wrapper method throws an exception.
If you take this approach, you’ll normally want to inherit privately from the other code class and just expose the interface that you want your application to use.
This approach gives you all the benefits of inheritance including the ability to override methods.
If you have to write an adapter that can go both ways, then you may want to use multiple public inheritance so that both sets of interfaces are available. Now if you have code that for whatever reason needs to be used in the sound library, then it can call your methods, catch any exceptions and return error codes instead of letting the exception continue beyond your wrapper.
Personally, I’ve never needed an adapter that can go both ways so this is probably not that common.
Your other choice is to use object composition where your wrapper just implements the interface your code expects and forwards any necessary calls to a member variable of the sound library class. This will normally be a pointer because that gives you the power to forward calls not just to some predefined sound library class but to any other sound library class that inherits from that class. You can’t get this level of customization when using inheritance unless you use templates. Maybe templates is exactly what you need but you’ll probably find that object composition gives you a lot of flexibility while still keeping your code simple.