CHAPTER 8
Adapter Pattern
This chapter covers the Adapter pattern.
GoF Definition
Convert the interface of a class into another interface that clients expect. The Adapter
pattern lets classes work together that could not otherwise because of incompatible
interfaces.
Concept
The core concept is best described by the following examples.
Real-Life Example
A common use of this pattern is when you use an electrical outlet adapter/AC power
adapter in international travels. These adapters can act as middlemen so that an
electronic device, say a laptop that accepts a U.S. power supply, can be plugged into a
European power outlet. Consider another example. Suppose you need to charge your
mobile phone. But you see that the electrical outlet is not compatible with your charger.
In this case, you may need to use an adapter. Even a translator who is converting one
language to another can be considered to be following this pattern in real life.
Now imagine a situation where you need to plug an application into an adapter
(which is X-shaped in this example) to use the intended interface. Without using this
adapter, you cannot join the application and the interface.
97
© Vaskaran Sarcar 2018
V. Sarcar, Design Patterns in C#, [Link]
Chapter 8 Adapter Pattern
Figure 8-1 shows the case before using an adapter.
Figure 8-1. Before using an adapter
Figure 8-2 shows the case after using an adapter.
Figure 8-2. After using an adapter
Computer World Example
Suppose you have an application that can be broadly classified into two parts: the user
interface (UI or front end) and the database (back end). Through the user interface,
clients can pass some specific type of data or objects. Your database is compatible with
those objects and can store them smoothly. Over a period of time, you may feel that you
need to upgrade your software to make your clients happy. So, you may want to allow
some other type of object also to pass through the UI. But in this case, the first resistance
will come from your database because it cannot store these new types of objects. In such
a situation, you can use an adapter that will take care of the conversion of these new
objects to a compatible form that your old database can accept.
A simple use of this pattern is described in the following example.
98
Chapter 8 Adapter Pattern
Illustration
In this example, you can calculate the area of a rectangle easily. In the Calculator class,
you need to supply a rectangle in the GetArea() method to get the area of the rectangle.
Now suppose you want to calculate the area of a triangle. But your constraint is that you
want to get the area of it through the GetArea() method of the Calculator class.
How can you achieve that?
To deal with this problem, you make a CalculatorAdapter for a triangle and pass
a triangle in its GetArea() method. In turn, the method will treat these triangles like
rectangles to get the areas of them from the GetArea() method of the Calculator class.
Class Diagram
Figure 8-3 shows the class diagram.
Figure 8-3. Class diagram
99
Chapter 8 Adapter Pattern
Directed Graph Document
Figure 8-4 shows the directed graph document.
Figure 8-4. Directed Graph Document
Solution Explorer View
Figure 8-5 shows the high-level structure of the parts of the program.
100
Chapter 8 Adapter Pattern
Figure 8-5. Solution Explorer View
101
Chapter 8 Adapter Pattern
Implementation
Here’s the implementation:
using System;
namespace AdapterPattern
{
class Rect
{
public double length;
public double width;
}
class Calculator
{
public double GetArea(Rect rect)
{
return [Link] * [Link];
}
}
class Triangle
{
public double baseT;//base
public double height;//height
public Triangle(int b, int h)
{
[Link] = b;
[Link] = h;
}
}
class CalculatorAdapter
{
public double GetArea(Triangle triangle)
{
Calculator c = new Calculator();
Rect rect = new Rect();
102
Chapter 8 Adapter Pattern
//Area of Triangle=0.5*base*height
[Link] = [Link];
[Link] = 0.5 * [Link];
return [Link](rect);
}
}
class Program
{
static void Main(string[] args)
{
[Link]("***Adapter Pattern Demo***\n");
CalculatorAdapter cal = new CalculatorAdapter();
Triangle t = new Triangle(20, 10);
[Link]("Area of Triangle is" + [Link](t) +
"Square unit");
[Link]();
}
}
}
Output
Here’s the output:
***Adapter Pattern Demo***
Area of Triangle is 100 Square unit
Modified Illustration
You have already seen a simple example of the Adapter design pattern. But if you want
to follow object-oriented design principles, you may feel that you need to modify the
implementation because you have learned that instead of using concrete classes, you
should always prefer to use interfaces. So, keeping this aim in mind, let’s modify the
illustration.
103
Chapter 8 Adapter Pattern
Key Characteristics of the Modified Implementation
Here are some key characteristics of the modified example:
• Class Rect is implementing RectInterface and the
CalculateAreaOfRectangle() method calculates the area of a
rectangle object.
• The Triangle class implements TriInterface and the
CalculateAreaOfTriangle() method calculates the area of a
triangle object.
• The constraint is that you need to calculate the area of the triangle
using RectInterface. To serve this purpose, you make an adapter
that can interact with RectInterface.
• Notice the beauty of using this pattern. Neither the Rectangle code
nor the Triangle code needs to change. You are using an adapter
that helps you to interact with RectInterface, and at high level,
it appears to you that by using a RectInterface method, you are
computing the area of a triangle.
• Notice that the GetArea(RectInterface r) method also does not
know that through TriangleAdapter it is getting a Triangle object
instead of a Rectangle object.
• Suppose you have a higher demand for how many rectangle
objects you have. With the Adapter pattern, you can use
some of the triangle objects that may behave like rectangle
objects. How? Well, when using the adapter, though you are
calling CalculateAreaOfRectangle(), it is actually invoking
CalculateAreaOfTriangle(). So, you can modify the method body
as per your needs. For example, you could multiply the triangle area
by 2.0 to get an area of 200 square units in this example (just like a
rectangle object with a length 20 of units and a width of 10 units). So,
this technique can help you when you need to deal with objects that
are not exactly the same but are similar.
104
Chapter 8 Adapter Pattern
Note In the context of the previous point, you should not make an attempt
to convert a circle to a rectangle (or do a similar type of conversion) to get an
area because they are totally different shapes. But in this example, I am talking
about triangles and rectangles because they have similarities, and areas can be
computed easily with minor changes.
Modified Solution Explorer View
Figure 8-6 shows the structure of the modified program.
Figure 8-6. Solution Explorer View
105
Chapter 8 Adapter Pattern
Modified Implementation
Here’s the modified implementation:
using System;
namespace AdapterPattern_Modified
{
interface RectInterface
{
void AboutRectangle();
double CalculateAreaOfRectangle();
}
class Rect : RectInterface
{
public double Length;
public double Width;
public Rect(double l, double w)
{
[Link] = l;
[Link] = w;
}
public double CalculateAreaOfRectangle()
{
return Length * Width;
}
public void AboutRectangle()
{
[Link]("Actually, I am a Rectangle");
}
}
interface TriInterface
{
void AboutTriangle();
double CalculateAreaOfTriangle();
}
106
Chapter 8 Adapter Pattern
class Triangle : TriInterface
{
public double BaseLength;//base
public double Height;//height
public Triangle(double b, double h)
{
[Link] = b;
[Link] = h;
}
public double CalculateAreaOfTriangle()
{
return 0.5 * BaseLength * Height;
}
public void AboutTriangle()
{
[Link]("Actually, I am a Triangle");
}
}
/*TriangleAdapter is implementing RectInterface.
So, it needs to implement all the methods defined
in the target interface.*/
class TriangleAdapter:RectInterface
{
Triangle triangle;
public TriangleAdapter(Triangle t)
{
[Link] = t;
}
public void AboutRectangle()
{
[Link]();
}
public double CalculateAreaOfRectangle()
{
107
Chapter 8 Adapter Pattern
return [Link]();
}
}
class Program
{
static void Main(string[] args)
{
[Link]("***Adapter Pattern Modified Demo***\n");
//CalculatorAdapter cal = new CalculatorAdapter();
Rect r = new Rect(20, 10);
[Link]("Area of Rectangle is :{0} Square unit",
[Link]());
Triangle t = new Triangle(20, 10);
[Link]("Area of Triangle is :{0} Square unit",
[Link]());
RectInterface adapter = new TriangleAdapter(t);
//Passing a Triangle instead of a Rectangle
[Link]("Area of Triangle using the triangle adapter
is :{0} Square unit", GetArea(adapter));
[Link]();
}
/*GetArea(RectInterface r) method does not know that through
TriangleAdapter, it is getting a Triangle instead of a Rectangle*/
static double GetArea(RectInterface r)
{
[Link]();
return [Link]();
}
}
}
108
Chapter 8 Adapter Pattern
Modified Output
Here’s the output:
***Adapter Pattern Modified Demo***
Area of Rectangle is :200 Square unit
Area of Triangle is :100 Square unit
Actually, I am a Triangle
Area of Triangle using the triangle adapter is :100 Square unit
Types of Adapters
The GoF described two types of adapters: class adapters and object adapters.
Object Adapters
Object adapters adapt through object compositions, as shown in Figure 8-7. So, the
adapter discussed so far is an example of an object adapter.
Figure 8-7. Object adapter
In this example, TriangleAdapter is the adapter that implements RectInterface
(Target interface). Triangle is the Adaptee interface. The adapter holds the Adaptee
instance.
109
Chapter 8 Adapter Pattern
Class Adapters
Class adapters adapt through subclassing and support multiple inheritance. But you
know that in C# multiple inheritance through classes is not supported. (You need
interfaces to implement the concept of multiple inheritance.)
Figure 8-8 shows the typical class diagram for class adapters, which support multiple
inheritance.
Figure 8-8. Class adapter
Q&A Session
1. How can you implement a class adapter design pattern in C#?
Answer:
You can subclass an existing class and implement the desired
interface. Here’s an example:
class ClassAdapter : Triangle, RectInterface
{
public ClassAdapter(double b, double h) : base(b, h)
{
}
public void AboutRectangle()
{
[Link]("Actually, I am an Adapter");
}
110
Chapter 8 Adapter Pattern
public double CalculateAreaOfRectangle()
{
return 2.0 * [Link]();
}
}
Note that this approach may not be applicable in all scenarios. For
example, you may need to adapt a method that is not specified in
a C# interface. In those cases, object adapters are useful.
2. Which one do you prefer, class adapters or object adapters?
Answer:
In most cases, I prefer compositions over inheritance. Object
adapters use compositions and are more flexible. Also, in many
cases, you may not implement a true class adapter (refer to the
answer of the previous question).
3. What are the drawbacks associated with this pattern?
Answer:
I do not see any big challenges. I believe that an adapter’s job
is simple and straightforward, but you may need to write some
additional code. However, the payoff is great, particularly for those
legacy systems that cannot be changed but you still want to use
them for their stability.
111