Hello, my name is Jonathan Boccara. I'm your host on Fluent C++. I have been a C++ developer for 6 years, working for Murex which is a major software editor in the finance industry. My focus is on C++ and particularly how to write expressive code. I'm happy to take your feedback, don't hesitate to drop a comment on a post, follow me or get in touch directly !
Jonathan Boccara's blog
Sometimes in our quest to writing expressive code we encounter dragons on our way. They can take the form of an old API, that seems to have been designed to make developers suffer, or even to have no design at all. You probably have already come across such APIs, haven’t you?
Some of these dragons we can slay by refactoring, but some are bigger than us (unless your job is to be a dragonslayer, and you can afford to spend your time getting rid of all evil APIs). Often we don’t have the time to repair all the APIs that we use, and to benefit from their features, we get little dragon burns that leave ugly scars in our code and our motivation as developers.
But there is another way. It consists of luring the beast into a cage while we walk past it, and let it blow its fire away. This is called the Adapter pattern. I’ll briefly show how it works if you haven’t seen it yet. But what I really want to show you is how the Adapter pattern can be modified to adapt wide-use APIs to your code, so that the dragon safely remains in its cage even when the next traveler walks by (and this could be you!). It’s not rocket science, but I have found it very useful.
Say that in our program, we have an API that is to be used at multiple places. Let’s take an example of an API that deals with modelling and simulating physical laws, used for representing how objects move in the application. This physical simulator can be used for modelling a car, a truck or a plane for instance. Although each vehicle has specific characteristics, their movements are determined by common physical laws, which are managed by this API.
But the problem is that this API is terribly designed/hard to use correctly/uses C idioms/is a heap of patches made by legacy code over time/uses globals/paste here your favorite code dragon.
And you need to use the features provided by this API in various part of the program. But by doing this, the low quality of the API will deteriorate the quality of your call site, and repeatedly over all the places it is used. That’s a problem.
The first thing we may think about when facing this situation is the Adapter pattern:
The Adapter pattern essentially consists in wrapping a hard to use API into a component that implements an easy to use one.
But here we need more than that: we don’t need just one easy to use component. Indeed, we have several pieces of specific code: the car, the truck and the plane, that need to feed very different inputs to the physical simulator API.
And we wouldn’t want the client code that uses a vehicle to directly interact with a physical simulator API, even if it presents a nice adapted interface. We want an additional level of indirection that deals with the specifics of each vehicle.
To achieve this, let’s pack the nice API with the adaption code to the ugly one into one component:
/* methods you would expect
in a physical simulator */
NicePhysicalSimulator(/* inputs you would
logically feed to a physical simulator */);
~NicePhysicalSimulator(); // see comments further below
This class encapsulates all the interactions with the old API: it takes natural inputs in its constructor, somehow initializes the old API with it, draws the results of the API and exposes them through its public interface.
EDIT: As suggested by Jens Weller,
NicePhysicalSimulator should rather hold
UglyPhysicalSimulator by pointer, with a only forward declaration. This is absolutely right and, as this needs a lot of details to implement correctly (in particular to use smart pointers), it is a whole topic by itself.
More on the choices of scopes (
private) in just a moment.
Then all classes using the physical simulator can inherit from this class, and provide their specific characteristics via the base class constructor:
class CarPhysicalSimulator : public NicePhysicalSimulator
CarPhysicalSimulator : NicePhysicalSimulator(/* physical
characteristics of a car */)
class PlanePhysicalSimulator : public NicePhysicalSimulator
PlanePhysicalSimulator : NicePhysicalSimulator(/* physical
characteristics of a plane */)
Note that the base class is not meant to be used polymorphically. It is just there to deal with the evil API and to offer a nice interface to the rest of the code. Inheritance is only used here as a natural way to factor code and interfaces.
The choice of scopes in the base class is important:
publicbecause the class is not meant to be instantiated directly (but rather inherited from).
publicto avoid the class being destroyed by a call to delete. Indeed, base classes generally offer a virtual destructor so that deleting a pointer to base class calls the destructor of the derived class. But since there is no virtual method in the class, making the destructor virtual would add a virtual pointer to each object. To avoid this, the
protecteddestructor prevents calls to delete from compiling altogether. We don’t need pointers to base class anyway here.
privately. But this supposes that you need to hold the old API during the whole lifetime of the object. But some APIs won’t need you to store anything, because they perform all their job in one initial call, or because they work with side effects.
Finally, why use inheritance here? Indeed, composition is generally prefered over inheritance, because it reduces class coupling. Plus there are no virtual functions here!
A good case for using inheritance is when classes have an ‘is-a’ relationship. And clearly, a
CarPhysicalSimulator is a
PhysicalSimulator. The practical consequence here is that by inheriting, the derived class automatically benefits from the public interface exposed by the base class. By using composition, we should have manually forwarded each of the methods of the interface to the contained adapter.
Hope this will be useful in keeping your code safe from evil dragon APIs, and let it become as expressive as can be!
Share this post!     Don't want to miss out ? Follow: