Adapter Design Pattern- C#
Introduction
Adapter is a structural design pattern that allows objects with incompatible interfaces to collaborate.
Let me try to explain in the layman terms. Electrical outlets provide electricity and different countries have differing interfaces. Appliances with one kind of plug may not be able to (safely) use incompatible outlets. An adapter is used to allow devices with incompatible interfaces to work together. A specific adapter works between two specific interfaces. Everyone must have faced this type of issue with our laptop chargers when we travel to different countries and they have different electrical sockets as compared to our laptop charger. So, What we do then? we buy socket adapters/converters for that.
Hence we can say that- Adapters convert the interface of one class into an interface a client expects.
Adapter Pattern- UML Diagram
Target
- This is an interface that is used by the client to achieve its functionality/request.
Adapter
- This is a class that implements the Target interface and inherits the Adaptee class. It is responsible for communication between Client and Adaptee.
Adaptee
- This is a class that has the functionality, required by the client. However, its interface is not compatible with the client.
Client
- This is a class that interacts with a type that implements the Target interface. However, the communication class called Adaptee is not compatible with the client
Implementation- C# Example
Problem: Let’s say we have a product that provides the list of grocery items in XML format to our clients. It was running fine for our existing clients. But now our business is growing and new clients are coming and they want the list of grocery items in JSON format. So, here we have a problem that our client has a different interface that requires JSON and our end product has a different interface that provides XML. Hence, here Adapter pattern will come for rescue.
Let’s see the existing code first.
Grocery Model-
namespace AdapterDesign.Models{ public class Grocery {
public string grocId { get; set; } public string qty { get; set; } public string price { get; set; } public string weight { get; set; }
}}
Grocery Manager- Which returns the XML Document
using AdapterDesign.Models;namespace AdapterDesign.BAL
{
public class GroceryManager
{
public XmlDocument GetXMLGroceries()
{
List<Grocery> groceries = new List<Grocery>();
groceries.Add(new Grocery {grocId = "KJA1", price = "136.00 USD", qty = "5", weight = "141.9 gm"}); groceries.Add(new Grocery { grocId = "TAD1", price = "243.35 USD", qty = "7", weight = "545.7 gm" }); groceries.Add(new Grocery { grocId = "ASD23", price = "45.00 USD", qty = "12", weight = "14.5 gm" }); groceries.Add(new Grocery { grocId = "PASJ2", price = "150.20 USD", qty = "8", weight = "800.0 gm" });
XElement xel= new XElement("Groceries",
from groc in groceries
select new XElement("Grocery",
new XAttribute("ID", groc.grocId),
new XElement("Price", groc.price),
new XElement("Qty", groc.qty),
new XElement("Weight", groc.weight)
));
XmlDocument xDoc = new XmlDocument();
xDoc.LoadXml(xel.ToString());
return xDoc;
}
}}
Client- Which consumes to get the XML data
public class GroceryforClients
{
public HttpResponseMessage Get()
{
GroceryManager grocManager = new GroceryManager();
return new HttpResponseMessage()
{ Content = new StringContent(grocManager.GetXMLGroceries().InnerXml,
Encoding.UTF8,
"application/xml")
};
}
}
Solution: For new clients to access the same data but having different interface we will introduce Adapter Design pattern.
Here the code goes,
Interface IXmltoJsonGrocery- Which will be exposed to Client to use the method
namespace AdapterDesign.BAL
{
interface IXmltoJsonGrocery
{
string getJsonGroceries();
}}
Adapter Class- Which will implement the interface and provides the implementation as per Client
namespace AdapterDesign.BAL
{
public class JsonGrocery : IXmltoJsonGrocery
{
private readonly GroceryManager _groc;
public JsonGrocery(GroceryManager groc)
{
_groc = groc;
}
public string getJsonGroceries()
{
XmlDocument doc = _groc.GetXMLGroceries();
return JsonConvert.SerializeXmlNode(doc, Newtonsoft.Json.Formatting.None, true);
}
}}
Finally, Client- which will consume the Interface through Adapter.
public class GroceryforClients
{
public HttpResponseMessage Get(int id)
{
GroceryManager grocManager = new GroceryManager();
return new HttpResponseMessage()
{
Content = new StringContent(
new JsonGrocery(grocManager).getJsonGroceries(),
Encoding.UTF8,
"application/json")
};
}
}
Explanation: We have used the Adapter Design Pattern in the above problem statement. Here,
- Interface- IXmltoJsonGrocery is Target.
- JsonGrocery class is our Adapter which is providing the functionality as per the Client’s interface(requirement) by deriving from the interface-IXmltoJsonGrocery.
- GroceryManager class which has method- GetXMLGroceries is our Adaptee which has different output (interface) as compared to our Client’s requirement.
- GroceryforClients class is our Client which wants the data and using Adapter to get the data from our Adaptee. In this case, if Client needs XML data it can directly use our GroceryManager class to get the data. But if client needs JSON data then client can use Adapter to get the data.
Key Points: Adapter Design pattern is of 2 types- Object Adapter & Class Adapter
- Object Adapter- In this pattern, Adapter uses object composition as we have done in the above example where we have used object of GroceryManager (Adaptee) to be passed in Adapter class through constructor.
- Class Adapter- In this pattern, Adapter uses inheritance. Please see below, our Adapter is not only going to implement Interface but also going to inherit from Adaptee so it will save the object creation.
public class JsonGrocery : GroceryManager, IXmltoJsonGrocery
{
public string getJsonGroceries()
{
XmlDocument doc = GetXMLGroceries();
return JsonConvert.SerializeXmlNode(doc, Newtonsoft.Json.Formatting.None, true);
}}
Client’s Implementation will also not require any object creation.
public HttpResponseMessage Get()
{
return new HttpResponseMessage()
{
Content = new StringContent(
new JsonGrocery().getJsonGroceries(),
Encoding.UTF8,
"application/json")
};}
When to use the Adapter Design Pattern in the real-time application?
We need to choose the Adapter Design Pattern in real-time applications when
- A class needs to be reused that does not have an interface that a client requires.
- Allow a system to use classes of another system that is incompatible with it.
- Allow communication between a new and already existing system which are independent of each other.
- Sometimes a toolkit or class library cannot be used because its interface is incompatible with the interface required by an application.
Summary
The Adapter pattern attempts to reconcile two incompatible interfaces and is especially useful when one or both of those interfaces cannot be refactored.
The repository for this pattern is over my GitHub and contains all of the sample code used here.
Happy Coding!!