Introduction

When you have an existing API that is heavily reliant on certain interfaces within it and a newer side-by-side (SxS) API comes into production that is reliant on very similar interfaces what do you do? You can’t throw the old code away as the bulk of your system is legacy code. However, you would like to use the more optimized algorithms presented in the newer API. No doubt you will go away and traverse various articles on the internet and those in a library to arrive at something called the adapter pattern. In this article we will be looking at the object adapter as the type adapter requires multiple inheritance.

The adapter pattern allows us to change the interface of a type to something that a client expects. If there is anything you take from this article the previous sentence would be a prime candidate, although I implore you to read on.

Design Patterns Series

Part 1 Learn how to design more robust and maintainable code by incorporating design patterns into your software projects.

Part 5 In this part of the design patterns series we investigate the command pattern and its uses.

Part 6 In this part of the design patterns series we will look at the adapter pattern.

Part 7 In this part of the design patterns series we will take a look at the template pattern.

The adapter pattern

We mentioned shortly ago that the adapter pattern changes the interface of a type to something that the client expects, how on earth can we do that? The adapter pattern is really very simple, however, before we proceed its best that we look at a few of the major roles within the adapter pattern.

There are four roles within the adapter pattern: client, adapter, adaptee, and the target interface. The client is a pseudoname for something that expects a specific interface (or type). We could for the purposes of simplicity state that the client is an alias for a method which expects as its parameter a type that implements a specific interface. As another example the client may need to collaborate with a type later on that is required to implement a specific interface. Next on the list is the big one – the adapter. What does the adapter do? Well, it wraps the adaptee up (explained shortly) and implements the interface the client expects. The adapter also delegates its work to the adaptee. An adaptee is the type that we want to use with the new code but we can’t because it doesn’t adhere to some behavior (as defined in an interface etc) that the client expects. Finally the target interface is the name given to the interface that the client expects. Don’t worry if all these roles have lost you at the moment, we will illustrate them soon via the use of a simple example scenario.

The scenario

The example to use for this pattern was really quite simple to think of as the problem is very common for anyone who travels abroad. The problem is that of plug sockets, namely that the UK plugs have three pins, and the continental (European, US) have two pins. For UK nationals (and others) that travel abroad this means that all our electrical devices like, for example mobile phone chargers, laptop power adapters and so on are pretty much useless. Fortunately for us, and for the rest of the world there exists adapters to make a three pin plug compatible with a two pin power source and vice versa for our international friends looking to dawn the UK shores. For this simple scenario let’s assume that we’ve taken only a few of our family to a beautiful resort someplace in the south of France. There is one problem though – the other family members stranded in the UK would like to know how we are getting on with our nice holiday. The problem we have is that the battery in our mobile phone has died and we only have a UK three pin adapter. Oh dear…

Rest assured though that a quick stroll down to the local supermarket has brought with it the purchase of a three-to-two pin adapter. Now we can phone home. Let’s go ahead and model this scenario using the adapter pattern.

Mapping the scenario to the roles within the adapter pattern

First let us define two interfaces, each of which contain the same method name and prototype. These interfaces will be called ITwoPinPlug, and IThreePinPlug. Both interfaces define a method named Utilize. We will also define a few types: TwoPinPlug, and ThreePinPlug. Each plug implements the appropriate interface.

To keep the sample code concise and to the point we will focus on the plugs and how we can adapt each to use the others respective power source. For this we will require two adapters: TwoPinPlugAdapter, and ThreePinPlugAdapter. The adapters will be composed of the correct respective plug type as well as implementing the opposing plugs interface.

The role of the TwoPinPlugAdapter, and the ThreePinPlugAdapter are indeed adapters. The adapters will implement one of the target interfaces – either ITwoPinPlug, or IThreePinPlug. The adaptee’s are the plugs that we want to change the interface of.

Figure 1: UML of our design

Now we are familiar with the design we will now present the implementation.

Listing 1: ITwoPinPlug.cs

Listing 2: IThreePinPlug.cs

Listing 3: TwoPinPlug.cs

Listing 4: ThreePinPlug.cs

Listing 5: TwoPinPlugAdapter.cs

Listing 6: ThreePinPlugAdapter.cs

Listing 7: TwoPinSource.cs

Listing 8: ThreePinSource.cs

Listing 9: Program.cs

If you run the project you will see the following output:

Figure 2: Output of the example

The example scenario presented is very simple in that the interfaces described contain the same methods of which both plug types implement, but what if the interfaces varied? You should only support the methods of the target interface that your adaptee type(s) know how to deal with. As a result you may find yourself declaring that the adaptee within the adapter doesn’t support a particular method defined in the target interface. This is of course is perfectly reasonable.

Consider a linked list with AddHead and AddTail operations. Furthermore, consider that we have a client that requires something that implements an interface IStack which declares the methods Push and Pop. In this particular example the adapter would implement IStack as the target interface with the adaptee being a linked list. In the Push method within the adapter type we would call the AddTail method, and in the Pop method we would call the RemoveTail method of the adaptee.

Summary

In this part of the design pattern series we have looked at the adapter pattern – a pattern that changes the interface of a type. We have identified and discussed the roles of the client, adapter, adaptee, and target interface. The culmination of our efforts led us to apply the adapter pattern to a simple plug scenario.

Acknowledgements

I would like to thank Jon Skeet [1] for reviewing this article and also providing further examples of the adapter pattern.