Adapter design pattern simplified

Shripad Jadhav
Globant
Published in
4 min readSep 22, 2022
Adapter design pattern

In this article, we will go through everything about the adapter design pattern. An adapter design pattern is a structural design pattern. We can also call it a wrapper.

Why do we need an Adapter design pattern?

Sometimes interfaces may not compatible with each other, to communicate between such interfaces we need to convert incompatible interface objects into compatible ones. Here adapter design pattern comes in handy, the adapter is a specially designed object that converts the interface of one object to another.

There are two ways we can implement the adapter design pattern
1. Class Adapter: A class adapter uses inheritance to adapt one interface to another.
2. Object Adapter: An object adapter relies on object composition.

Class Adapter

Let’s take an example of a DC motor, that needs a DC power connection but somehow we have an electricity AC connection. In order to start the DC motor, we need to convert the AC connection to DC.

Class Diagram — Class Adapter

Implementation.

Step 1: Define a Connection interface that satisfies the requirement of the DC motor

interface Connection {
fun getEletricity(): DCConnection
}

Step 2: Now DC motor class takes Connection as the input parameter

open class DCMotor(connection:Connection){
fun startMotor(){
val electricity = connection.getEletricity()
.....
.....
}
}

In contrast, ACConnection and DCConnection classes are as follows

open class ACConnection {
fun getConnection():ACConnection{
returns acConnection
}
}
open class DCConnection {
fun getConnection():DCConnection{
returns dcConnection
}
}

AC Connection is not compatible with DC motor, covert the AC connection to DC with the help of an Adapter pattern.

Step 3: A Class adapter uses inheritance to adapt interfaces. The getEletricity() operation converts ACConnection to DCConnection, to conform Connection interface requirement.

Finally, we define getEletricity() which converts AC connection to DC.

class ACToDCConverter : Connection, ACConnection {
fun getEletricity(): DCConnection{
val ac = getEletricity()
//convert AC to DC
.......
.......
Return dcConnection
}
}

Alternatively in Kotlin, we can use the extension functions to implement class adapters behavior.

fun ACConnection.toDCConnection() : DCConnection {
return object : DCConnection {
// Do something to convert
}
}

Object Adapter

The Object Adapter uses object composition to combine classes with different interfaces.

Object Adapter class diagram

Implementation.
Let’s implement same with an Object Adapter. In this approach, the adapter ACToDCObjectAdapter maintains a pointer to acConnection.

class ACToDCObjectAdapter(acConnection:AcConnection):DCConnection {
fun getEletricity(): DCConnection{
val ac = acConnection.getConnection()
//convert AC to DC
.......
.......
Return dcConnection
}
}

How does it work?

Clients call operations on an adapter instance. In turn, the adapter calls adaptee (incompatible class) operations that carry out the request.

class AdapterDemo {
fun main(){
//Class Adapter
val acToDCClassAdapter = ACToDCConverter()
val dcMotor = DCMotor(acToDCClassAdapter)
dcMotor.startMotor()

//Object Adapter
val acConnection = ACConnection()
val acToDCObjectAdapter = ACToDCObjectAdapter(acConnection)
val dcMotor1 = DCMotor(acToDCClassAdapter)
dcMotor1.startMotor()
}
}

When we can use it?

1. Interface isn’t compatible with the rest of your code. E.g we use XML to JSON parsers, Mapper classes, to convert one data type to another.

2. When you want to reuse several existing subclasses that lack some common functionality that can’t be added to the superclass. You can wrap the subclass into the adapter and add the missing functionality.

Advantages

Code Separation: You can separate the interface or data conversion code from the primary business logic of the program.

Scalability: You can introduce new types of adapters into the program without breaking the existing client code, as long as they work with the adapters through the client interface.

Reusability: An object adapter allows a single adapter to work with many Adaptees that is, the Adoptee itself and all of its subclasses (if any). The Adapter can also add functionality to all Adaptees at once.

Extendability: A class adapter override some of Adaptee’s behavior since Adapter is a subclass of Adoptee.

Takeaways

When we can’t modify/adjust the two incompatible interfaces, here the adapter design pattern comes in handy.
But, creating the adapter even if you can modify/adjust the existing class or interface can be considered an anti-pattern since it creates overheads/redundant classes.

It’s all about the Adapter design pattern. I hope the article was informative.
Thank you! ✌️

Special thanks to Rohit Chandekar and Prerana Pawar for helping me to write this article.

--

--