Back to: Design Patterns in C# With Real-Time Examples
Adapter Design Pattern in C# with Examples
In this article, I am going to discuss the Adapter Design Pattern in C# with Examples. Please read our previous article where we discussed the basics of Structural Design Patterns. The Adapter Design Pattern falls under the category of Structural Design Pattern. As part of this article, we are going to discuss the following pointers.
- What is the Adapter Design Pattern?
- Understanding the Object and Class Adapter Design Pattern.
- Implementing both Class and Object Adapter Design Patterns using C#.
- When to use the Adapter Design Pattern in Real-Time Applications?
- When to use the Object Adapter Pattern and when to use the Class Adapter Pattern in C#?
What is Adapter Design Pattern?
The Adapter Design Pattern is a Structural Design Pattern that allows incompatible interfaces (objects) to work together. The Adapter Design Pattern acts as a bridge between two incompatible objects. Let’s say the first object is A and the second object is B. And object A wants to consume some of the services provided by object B. As they are incompatible so they cannot communicate directly. In this case, Adapter will come into the picture and will act as a middleman or bridge between object A and object B. Now, object A will call the Adapter and Adapter will do the necessary transformations or conversions and then it will call object B.
The Adapter Design Pattern in C# involves a single class called Adapter which is responsible for communication between two independent or incompatible interfaces. So, in simple words, we can say that the Adapter Design Pattern helps two incompatible interfaces to work together. We can also say that it works like a Wrapper which makes two incompatible systems work together. If this is not clear at the moment then don’t worry we will understand this with an example.
Example to Understand Adapter Design Pattern in C#:
Let us understand the Adapter Design Pattern with an Example and then we will see the UML Diagram of the Adapter Design Pattern by comparing it with our Example. Please have a look at the following image. Here, you can see two interfaces or you can say two systems. On the right-hand side, you can see the Third Party Billing System and on the left-hand side, you can see the Client i.e. the Existing HR System. Now, we will see how these two systems are incompatible and we will also see how we will make them compatible using Adapter Design Patterns in C#.
As you can see, the Third Party Billing System provides one functionality called ProcessSalary. What this ProcessSalary method will do is, it will take the employee list (i.e. List<Employee>) as an input parameter and then loop through each employee and calculate the salary and deposit the salary into the employee’s bank account.
On the left-hand side i.e. in the Existing HR System, the employee information is stored in the form of a string array. The HR System wants to process the salary of employees. Then what the HR System has to do is, it has to call the ProcessSalary method of the Third Party Billing System. But if you look at the HR system, the employee information is stored in the form of a string array and the ProcessSalary method of the Third Party Billing System wants data in List<Employee>. So, the HR System cannot call directly to the Third Party Billing System because List<Employee> and string array are not compatible. So, these two systems are incompatible.
How we can make these two incompatible systems work together?
We can use the Adapter Design Pattern in C# to make these two systems or interfaces work together. Now, we need to introduce an Adapter between the HR System and the Third Party Billing System as shown in the below image.
Now the HR System will send the employee information in the form of a String Array to the Adapter. Then what this Adapter will do is, it will read the employee information from the string array and populate the employee object and then put each employee object into the List<Employee> collection, and then the Adapter will send the List<Employee> to the ProcessSalary method of Third Party Billing System. Then the ProcessSalary method calculates the Salary of each employee and deposits the salary into the Employee’s bank account.
So, in this way, we can make two incompatible interfaces work together with the help of the Adapter Design Pattern in C#. Again the Adapter Design Pattern in C# can be implemented in two ways. They are as follows.
- Object Adapter Pattern
- Class Adapter Pattern
Note: We will discuss both the Object Adapter Pattern and Class Adapter Pattern as well as we will also discuss the difference between them and when to use one over another.
Implementation of Object Adapter Design Pattern in C#:
Let us implement the example that we discussed using Object Adapter Design Pattern in C# step by step. Once we discussed the Example, then we will see the UML Diagram of the Object Adapter Design Pattern by comparing it with our Examples. So, let us proceed and see how we can implement the Object Adapter Design Pattern in C#.
Step 1: Creating Employee Class
Create a class file with the name Employee.cs and then copy and paste the following code into it. This class is going to be used by ThirdPartyBillingSystem (i.e. Adaptee) as well as by the Adapter. Here, we created the Employee with the required properties and then initialize the properties using the class constructor.
namespace AdapterDesignPattern { public class Employee { public int ID { get; set; } public string Name { get; set; } public string Designation { get; set; } public decimal Salary { get; set; } public Employee(int id, string name, string designation, decimal salary) { ID = id; Name = name; Designation = designation; Salary = salary; } } }
Step2: Creating Adaptee
This is going to be a class that contains the functionality that is required by the client. However, this interface is not compatible with the client. So, create a class file with the name ThirdPartyBillingSystem.cs and then copy and paste the following code into it. This class is having the ProcessSalary method which takes a list of employees as an input parameter and then processes the salary of each employee.
using System; using System.Collections.Generic; namespace AdapterDesignPattern { // The Adaptee contains some functionality that is required by the client. // But this interface is not compatible with the client code. public class ThirdPartyBillingSystem { //ThirdPartyBillingSystem accepts employee's information as a List to process each employee's salary public void ProcessSalary(List<Employee> listEmployee) { foreach (Employee employee in listEmployee) { Console.WriteLine("Rs." + employee.Salary + " Salary Credited to " + employee.Name + " Account"); } } } }
Step3: Creating ITarget interface
This is going to be the domain-specific interface that is going to be used by the client. So, create an interface with the name ITarget.cs and then copy and paste the following code into it. This class defines the abstract ProcessCompanySalary method which is going to be implemented by the Adapter. Again the client is going to use this method to process the salary.
namespace AdapterDesignPattern { // The ITarget defines the domain-specific interface used by the client code. // This interface needs to be implemented by the Adapter. // The client can only see this interface i.e. the class which implements the ITarget interface. public interface ITarget { void ProcessCompanySalary(string[,] employeesArray); } }
Step4: Create an Adapter
This is going to be the class that implements the ITarget interface as well as has a reference to the Adaptee (ThirdPartyBillingSystem) object as we are using Object Adapter Design Pattern. This class is responsible for communication between the Client and Adaptee.
So, create a class file with the name EmployeeAdapter.cs and then copy and paste the following code into it. This class implements the ITarget interface and provides the implementation for the ProcessCompanySalary method. This class also has a reference to the ThirdPartyBillingSystem (Adaptee) object. The ProcessCompanySalary method receives the employee information as a string array and then converts the string array to a list of Employees and then calls the ProcessSalary method on the ThirdPartyBillingSystem (Adaptee) object by providing the list of employees as an argument.
using System; using System.Collections.Generic; namespace AdapterDesignPattern { // This is the class that makes two incompatible interfaces or systems work together. // The Adapter makes the Adaptee's interface compatible with the Target's interface. public class EmployeeAdapter : ITarget { //To use Object Adapter Design Pattern, we need to create an object of ThirdPartyBillingSystem ThirdPartyBillingSystem thirdPartyBillingSystem = new ThirdPartyBillingSystem(); //The following will accept the employees in the form of string array //Then convert the employee string array to List of Employees //After conversation, it will call the Adaptee's Method to Process the Salaries public void ProcessCompanySalary(string[,] employeesArray) { string Id = null; string Name = null; string Designation = null; string Salary = null; List<Employee> listEmployee = new List<Employee>(); for (int i = 0; i < employeesArray.GetLength(0); i++) { for (int j = 0; j < employeesArray.GetLength(1); j++) { if (j == 0) { Id = employeesArray[i, j]; } else if (j == 1) { Name = employeesArray[i, j]; } else if (j == 2) { Designation = employeesArray[i, j]; } else { Salary = employeesArray[i, j]; } } listEmployee.Add(new Employee(Convert.ToInt32(Id), Name, Designation, Convert.ToDecimal(Salary))); } Console.WriteLine("Adapter converted Array of Employee to List of Employee"); Console.WriteLine("Then delegate to the ThirdPartyBillingSystem for processing the employee salary\n"); thirdPartyBillingSystem.ProcessSalary(listEmployee); } } }
Step5: Client
Here, the client is going to be our HR System (i.e. the Main method of the Program class). Please modify the Main method as shown below. Notice that, here we have the employee information in the form of a string array. Then we create an instance of EmployeeAdapter and call the ProcessCompanySalary method by passing the string array as an argument. So, with the help of the Adapter (i.e. EmployeeAdapter object), now the Client and the Third Party Billing System work together.
using System; namespace AdapterDesignPattern { //Client //The Client is Incompatible with ThirdPartyBillingSystem class Program { static void Main(string[] args) { //Storing the Employees Data in a String Array string[,] employeesArray = new string[5, 4] { {"101","John","SE","10000"}, {"102","Smith","SE","20000"}, {"103","Dev","SSE","30000"}, {"104","Pam","SE","40000"}, {"105","Sara","SSE","50000"} }; //The EmployeeAdapter Makes it possible to work with Two Incompatible Interfaces Console.WriteLine("HR system passes employee string array to Adapter\n"); ITarget target = new EmployeeAdapter(); target.ProcessCompanySalary(employeesArray); Console.Read(); } } }
Output:
UML Diagram of Object Adapter Design Pattern in C#:
Now, let us understand the UML Diagram of the Object Adapter Design Pattern in C# by comparing the components with our example. In order to understand the Class or UML diagram and the different components involved in the Object Adapter Design Pattern, please have a look at the following diagram. As you can see, the client uses the ITarget Interface and create an instance of the Adapter, and using the Adapter instance the client communicates with the Adaptee. The Adapter is the component that makes it possible to work with two different incompatible interfaces.
The Adapter Design Pattern is composed of four components. They are as follows:
- Client: The Client class can only see the ITarget interface i.e. the class which implements the ITarget interface i.e. Adapter (in our example it is the EmployeeAdapter). Using that Adapter (EmployeeAdapter) object, the client will communicate with the Adaptee which is not compatible with the client.
- ITarget: This is going to be an interface and this interface needs to be implemented by the Adapter. The client can only see this interface i.e. the class which implements this interface.
- Adapter: This is the class that makes two incompatible interfaces or systems work together. The Adapter class implements the ITrager interface and provides the implementation for the interface method. This class is also composed of the Adaptee i.e. it has a reference to the Adaptee object as we are using Object Adapter Design Pattern. In our example, it is the EmployeeAdapter class that implements the ITarget Interface and provides implementations to the ProcessCompanySalary method of the ITarget Interface. This class also has a reference to the ThirdPartyBillingSystem object.
- Adaptee: This class contains the functionality which the client requires but it is not compatible with the existing client code. So, it requires some adaptation or some kind of transformation before the client can use it. It means the client will call the Adapter and the Adapter will do the required conversions if required and then it will make a call to the Adaptee.
This is all about the Object Adapter Design Pattern in C#. Let us proceed and see how to achieve the same thing using the Class Adapter Design Pattern in C#.
Understanding Class Adapter Design Pattern in C#:
This is another approach to implementing the Adapter Design Pattern in C#. In this approach, the Adapter calls will implement the ITarget interface as well as inherits from the Adaptee class. That means, now the Adapter class will be a child class of the Adaptee class. So, instead of creating a reference variable of Adaptee to call the Adaptee method, now it can call that method directly as that method is available via inheritance. Before implementing the same example using the Class Adapter Design Pattern, let us first understand the class diagram of the Class Adapter Design Pattern. Please have a look at the following image.
The class diagram is the same as the Object Adapter Design Pattern class diagram. The only difference is that the Adapter class now implements the Target interface and is inherited from the Adaptee class. In the case of the Object Adapter Design Pattern, the adapter has a reference to the Adaptee object, and using that reference, it will call the adaptee methods. But in the case of the Class Adapter Design Pattern, the adapter will call the inherited method of the Adaptee class directly.
Implementation of Class Adapter Design Pattern in C#:
Let us implement the previous example using the Class Adapter Design Pattern in C# step by step. The implementation is exactly the same as the Object Adapter Design Pattern Implementation. The only difference is in the EmployeeAdapter class. Now, in the EmployeeAdapter class, instead of Creating a reference variable of ThirdPartyBillingSystem class, we will make this EmployeeAdapter class inherits from the ThirdPartyBillingSystem, and after the required transformation or conversations, we just need to call the ProcessSalary method.
So, please modify the EmployeeAdapter class as shown below to use the Class Adapter Design Pattern in C#. Now, the EmployeeAdapter class is inherited from the Adaptee i.e. ThirdPartyBillingSystem class, and implements the ITarget interface.
using System; using System.Collections.Generic; namespace AdapterDesignPattern { // This is the class that makes two incompatible interfaces or systems work together. // The Adapter makes the Adaptee's interface compatible with the Target's interface. // To use Class Adapter Pattern, we need to inherit the Adapter class from the Adaptee class public class EmployeeAdapter : ThirdPartyBillingSystem, ITarget { //The following will accept the employees in the form of string array //Then convert the employee string array to List of Employees //After conversation, it will call the Adaptee's Method to Process the Salaries public void ProcessCompanySalary(string[,] employeesArray) { string Id = null; string Name = null; string Designation = null; string Salary = null; List<Employee> listEmployee = new List<Employee>(); for (int i = 0; i < employeesArray.GetLength(0); i++) { for (int j = 0; j < employeesArray.GetLength(1); j++) { if (j == 0) { Id = employeesArray[i, j]; } else if (j == 1) { Name = employeesArray[i, j]; } else if (j == 2) { Designation = employeesArray[i, j]; } else { Salary = employeesArray[i, j]; } } listEmployee.Add(new Employee(Convert.ToInt32(Id), Name, Designation, Convert.ToDecimal(Salary))); } Console.WriteLine("Adapter converted Array of Employee to List of Employee"); Console.WriteLine("Then delegate to the ThirdPartyBillingSystem for processing the employee salary\n"); //Call the Base Class ProcessSalary Method to Process the Salary ProcessSalary(listEmployee); } } }
Output:
When to use the Object Adapter pattern and when to use the Class Adapter Pattern in C#?
It is completely based on the situation. For example, if you have a java class and you want to make it compatible with the dot net class, then you need to use the Object Adapter Design Pattern and the reason is it is not possible to make inheritance. On the other hand, if both the classes are within the same project and using the same programming language, and if inheritance is possible. then you need to go for Class Adapter Design Pattern.
When to use the Adapter Design Pattern in Real-Time Applications?
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 that is 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.
In the next article, I am going to discuss one of the best Real-Time Examples of Adapter Design Patterns in C#. Here, in this article, I try to explain the Adapter Design Pattern in C# with Examples. I hope you understood the need and use of the Adapter Design Pattern in C# with Examples.