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 2 This part shows the observer design pattern.
Part 3 In this part you will learn about the factory pattern.
Part 4 This part of the design patterns covers the singleton pattern.
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 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
IThreePinPlug. Both interfaces define a method named
Utilize. We will also define a few types:
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:
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
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
AddTail operations. Furthermore, consider that we have a client that requires something that implements an interface
IStack which declares the methods
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.
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.
I would like to thank Jon Skeet  for reviewing this article and also providing further examples of the adapter pattern.
Sorry, no bio is available
This author has published 32 articles on DotNetSlackers. View other articles or the complete profile here.
Other articles in this category
Introduction to StructureMap
Have you heard of StructureMap, generally know what it’s for, and want to know how to get started qu...
DI Patterns: Constructor Injection
In this article, an excerpt from the book "Dependency Injection in .NET", we will take a detailed lo...
The Command Pattern
In this article I will provide a quick refresher on what the command pattern is used for, how it wor...
Brian Mains discusses how to implement the Arrange-Act-Assert pattern in TypeMock.
Key Process Patterns
This article, based on chapter 2 of Specification by Example, presents effective patterns for softwa...
Please login to rate or to leave a comment.