What is Adapter Design Pattern?
The Adapter design pattern is one of the most common and useful structural design patterns. It allows objects with incompatible interfaces to?collaborate with each other. This design pattern involves a single class called adapter which is responsible for communication between two independent or incompatible interfaces.
In the above image, on the right-hand side, we have a third-party Analytics library that consumes data in JSON format, and on the left-hand side, we have a client application that generates stock data in XML format. We can easily see that these two systems are incompatible and that’s why we have an XML to JSON Adapter in the middle that can facilitate the communication between these two systems.
The Adapter Design Pattern is composed of the following components:
Pros and Cons of Adapter Pattern
Pros:
Cons:
Adapter Pattern?Implementation?Techniques
The Adapter Design Pattern can be implemented in two ways
In this post, I will show you how to implement the Adapter pattern using both?Object?and?Class?adapter patterns with a real-world example.
Implementing Adapter Pattern using Object Adapter
Using this technique, the?Adapter?class implements the?Target?interface but doesn’t inherit the?Adaptee?class. The?Adaptee?object is passed into the constructor of the?Adapter?class. The?Adapter?class then perform all the conversions and used the?Adaptee?object to delegate all requests to Adaptee. The main advantage of implementing an adapter pattern using the Object Adapter technique is that you can implement (adapt) multiple adapters to the same target.
Let’s learn more about the Object Adapter pattern by implementing a complete example. In this example, I will create two separate classes which are not compatible with each other, and then we will introduce an adapter in the middle of these two classes to facilitate communication.
Step 1: Implementing Client
Create a new ASP.NET Core 5 MVC web application and add the following?Employee?class in the?Models?folder.
Employee.cs
? ? public class Employee
? ? {
? ? ? ? public int Id { get; set; }
? ? ? ? public string Name { get; set; }
? ? ? ? public decimal Salary { get; set; }
? ? ? ??
? ? ? ? public Employee(int id, string name, decimal salary)
? ? ? ? {
? ? ? ? ? ? Id = id;
? ? ? ? ? ? Name = name;
? ? ? ? ? ? Salary = salary;
? ? ? ? }
? ? }
Create an interface and a service that will return the list of employees to our existing system.
IEmployeeService.cs
? ? public interface IEmployeeService
? ? {
? ? ? ? List<Employee> GetEmployees();
? ? }
For the demo purpose, let’s return a fake list of employees from the service.
EmployeeService.cs
? ? public class EmployeeService : IEmployeeService
? ? {
? ? ? ? public List<Employee> GetEmployees()
? ? ? ? {
? ? ? ? ? ? return new List<Employee>()
? ? ? ? ? ? {
? ? ? ? ? ? ? ? new Employee(1, "James", 20000),
? ? ? ? ? ? ? ? new Employee(2, "Peter", 30000),
? ? ? ? ? ? ? ? new Employee(3, "David", 40000),
? ? ? ? ? ? ? ? new Employee(4, "George", 50000)
? ? ? ? ? ? };
? ? ? ? }
? ? }
Register the service with .NET Core DI Container by adding the following line in the?ConfigureServices?method of?Startup.cs?file.
services.AddScoped<IEmployeeService, EmployeeService>();
Inject the?IEmployeeService?in the constructor of the?HomeController.cs?file and call the?GetEmployees?method in the?Get method.
HomeController.cs
领英推荐
? ? [ApiController]
? ? [Route("[controller]")]
? ? public class HomeController : ControllerBase
? ? {
? ? ? ? private readonly IEmployeeService _employeesService;
? ? ? ? public HomeController(IEmployeeService employeesService)
? ? ? ? {
? ? ? ? ? ? _employeesService = employeesService;
? ? ? ? }
? ? ? ? [HttpGet]
? ? ? ? public IActionResult Get()
? ? ? ? {
? ? ? ? ? ? return Ok(_employeesService.GetEmployees());
? ? ? ? }
? ? }
Step 2: Implementing Adaptee Object
Let’s assume that you need to submit the above list of employees to a third-party analytical service that wants to run generate some analytical reports using the employee’s data. Create the following interface and service that will act as an Adaptee to whom we want to send employees data from our application.
IAnalyticsService.cs
? ? public interface IAnalyticsService
? ? {
? ? ? ? void GenerateReport(string json);
? ? }
AnalyticsService.cs
? ? public class AnalyticsService : IAnalyticsService
? ? {
? ? ? ? public void GenerateReport(string json)
? ? ? ? {
? ? ? ? ? ? // Process json to generate report.
? ? ? ? }
? ? }
You can immediately see the problem in the above service. The?GenerateReport?method of this so-called third-party analytical service needs employee data as JSON string but in your application, the employees data is available as a?List<Employee>.
Register the service in?Startup.cs?file as follows:
services.AddScoped<IAnalyticsService, AnalyticsService>();
Let’s try to use the?AnalyticsService?in our?HomeController?by injecting the service into the constructor and then calling the?GenerateReport?method.
? ? [ApiController]
? ? [Route("[controller]")]
? ? public class HomeController : ControllerBase
? ? {
? ? ? ? private readonly IEmployeeService _employeesService;
? ? ? ? private readonly IAnalyticsService _analyticsService;
? ? ? ? public HomeController(IEmployeeService employeesService
? ? ? ? ? ? , IAnalyticsService analyticsService
? ? ? ? ? ? )
? ? ? ? {
? ? ? ? ? ? _employeesService = employeesService;
? ? ? ? ? ? _analyticsService = analyticsService;
? ? ? ? }
? ? ? ? [HttpGet]
? ? ? ? public IActionResult Get()
? ? ? ? {
? ? ? ? ? ? return Ok(_employeesService.GetEmployees());
? ? ? ? }
? ? ? ? [HttpGet]
? ? ? ? public IActionResult GenerateReport()
? ? ? ? {
? ? ? ? ? ? var employees = _employeesService.GetEmployees();
? ? ? ? ? ? _analyticsService.GenerateReport(employees);
? ? ? ? ? ? return Ok();
? ? ? ? }
? ? }
We should see the following error in the above code because clearly, we are passing the?List<Employee>?to?GeneateReport?method that expects employees as a JSON string.
We now have a real-world example of two incompatible classes which can’t communicate with each other. We are now ready to implement our Target interface and an intermediary adapter class to solve the above problem.
Step 3: Implementing Target Interface
Create the following?IAnalyticsAdapter?interface that will be implemented by the Adapter.
IAnalyticsAdapter.cs
? ? public interface IAnalyticsAdapter
? ? {
? ? ? ? void ProcessEmployees(List<Employee> employees);
? ? }
Step 4: Implementing Adapter
Create the following?AnalyticsAdapter?class that will implement the?IAnalyticsAdapter?interface defined above. This class also has a reference to our fictitious third-party service?IAnalyticsService?object. The?ProcessEmploees?method receives the employees as a?List<Employee>?and then serializes the list to a JSON string. Finally, it calls the same?GenerateReport?method on the?IAnalyticsService?object that we were calling from our?HomeController?(Client) above.
AnalyticsAdapter.cs
? ? public class AnalyticsAdapter : AnalyticsService, IAnalyticsAdapter
? ? {
? ? ? ? public void ProcessEmployees(List<Employee> employees)
? ? ? ? {
? ? ? ? ? ? var json = System.Text.Json.JsonSerializer.Serialize(employees);
? ? ? ? ? ? GenerateReport(json);
? ? ? ? }
? ? }
Register the Adapter in the?Startup.cs?file using the following code.
services.AddScoped<IAnalyticsAdapter, AnalyticsAdapter>();
We need to modify the code of our?HomeController?to start using our adapter. We will not communicate with the incompatible?IAnalyticsService?directly that’s why we need to replace the?IAnalyticsService?with the?IAnalyticsAdapter?object as shown in the code below. We will pass the list of employees to the adapter?ProcessEmployees?method and this method will call the GenerateReport method of the?IAnalyticsService?internally.
HomeController.cs
? ? [ApiController]
? ? [Route("[controller]")]
? ? public class HomeController : ControllerBase
? ? {
? ? ? ? private readonly IEmployeeService _employeesService;
? ? ? ? private readonly IAnalyticsAdapter _analyticsAdapter;
? ? ? ? // private readonly IAnalyticsService _analyticsService;
? ? ? ? public HomeController(IEmployeeService employeesService
? ? ? ? ? ? , IAnalyticsAdapter analyticsAdapter
? ? ? ? ? ? //, IAnalyticsService analyticsService
? ? ? ? ? ? )
? ? ? ? {
? ? ? ? ? ? _employeesService = employeesService;
? ? ? ? ? ? _analyticsAdapter = analyticsAdapter;
? ? ? ? ? ? //_analyticsService = analyticsService;
? ? ? ? }
? ? ? ? [HttpGet]
? ? ? ? public IActionResult Get()
? ? ? ? {
? ? ? ? ? ? return Ok(_employeesService.GetEmployees());
? ? ? ? }
? ? ? ? [HttpGet]
? ? ? ? public IActionResult GenerateReport()
? ? ? ? {
? ? ? ? ? ? var employees = _employeesService.GetEmployees();
? ? ? ? ? ? //_analyticsService.GenerateReport(employees);
? ? ? ? ? ? _analyticsAdapter.ProcessEmployees(employees);
? ? ? ? ? ? return Ok(_employeesService.GetEmployees());
? ? ? ? }
? ? }
Conclusion
In the modern world of cloud computing and distributed applications, the Adapter pattern is a very commonly used pattern by C# and Java developers. We can use this pattern whenever we want to communicate with third-party libraries or systems which are not compatible with our system. I hope you have found this post useful. If you have any comments or suggestions, please leave your comments below. Don’t forget to share this tutorial with your friends or community.
See source code.