Adapter Design Pattern
The word Adapt means suitable for a new use.
Let’s take an example from our homes, we all have seen electric sockets mounted on our walls. There is a strange fact about these sockets that every country has its own style of electric outlets. That’s why smartphone manufacturers make a different style of charging adapters that are specific to the country.
Another great example would be Apple’s Hyperdrive. It connects so many devices to a single USB port. Here, hyperdrive is acting as an Adapter.
The adapter also is known as a wrapper. cause it wraps objects that can be used by different objects.
Let’s see UML first.
- The
client
is basically the class who wants to use the pattern. The calling class. - The
client
wants to haveSpecific_request()
which can only be served byclass Adaptee
, we can directly have a HAS-A relationship and be done with it. But the problem lies when there is another specific_request which is served by let’s say anotherclass Adaptee2
, in this case, we have to make another HAS-A relationship and this could reoccur with every new requirement. So you see the problem, every time new request comes we have to map that to client class, now you’d argue what’s wrong with that approach well if you do that for every single request your code won’t be extensible as per “Open-Closed principle” we are Closed for Modification.
So how do we get past this before getting into too much of a mess!!
To deal with this chaos, we can abstract a concept of a new request and let abstraction handle delegation of a request made by the client. In the above UML, it is handled by the interface ITarget
.
Let’s jump into coding.
Example, Let’s say our application reads data from an XML file and it serves the purpose for today but if tomorrow, we would like our application to support JSON, then what? That’s when we can use Adapter design pattern.
Let’s design the architecture for this problem. In the below diagram, we have 2 classes XMLAdaptee
and JSONAdaptee
, these classes are going to inherit the interface IEmployeeDetails
which has an abstract method GetData()
, so our classes can use their own implementation for their own logic.
The class program
can inject XMLAdaptee’s
instance to the Adapter object
if it wants result in XML or it can pass JSONAdaptee’s
instance if it wants result in JSON.
Now if tomorrow another requirement comes for YAML then we can simply create class YAMLAdaptee
and write it’s own logic in GetData() method
.
This is how our UML would look:
Let’s create an interface ITarget
which will be called by our client.
Next, class Adapter
,
which will implement ITarget
: this class also has interface IEmployeeDetails
: this interface will be implemented by both of our XML & JSON Adaptees to keep similar architecture in both of the classes, so our client code won’t change.
Let’s create an interface IEmployeeDetails
:
An class employee
and entity:
An class XMLAdaptee
to load xml data. For now, we can generate data from code itself.
Next, class JSONAdaptee
:
Last but not least, our caller class program
:
Output for XML data: line number 15 in program.cs
Output for JSON data, just uncomment the above line in the code. line number 18 in program.cs
Perfect! I sincerely hope you enjoyed this article and that you’re inspired to apply what you’ve learned to your own applications. Thank you.
Happy coding!!
Catch me on Linkedin