What is Adapter Design Pattern?

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:

  1. Client:?A class that wants to consume an incompatible third-party application.
  2. Target: An interface that needs to be implemented by the Adapter and used by the client. The client class can see only this interface and don’t directly communicate with third party application.
  3. Adapter: A class that makes two incompatible systems work together. The Adapter class implements the Target interface also has the reference to the Adaptee object.
  4. Adaptee: A class that contains the functionality requires by the client but it’s not compatible with the existing client code. This class needs to be adopted by the adapter before the client can use it. In simple words, the client will call the Adapter and the Adapter will do the conversion if required and then it will make a call to the Adaptee.

Pros and Cons of Adapter Pattern

Pros:

  • It encourages the Single Responsibility Principal because we can separate the conversion code from the primary business logic of the program.
  • It increases the transparency and reusability of the class because the specific implementation is encapsulated in the adapter class which is transparent to the client class.
  • We can introduce new types of adapters without breaking the existing client code.

Cons:

  • Too many adapters make the system messy and difficult to understand and navigate as a whole.

Adapter Pattern?Implementation?Techniques

The Adapter Design Pattern can be implemented in two ways

  1. Object Adapter
  2. Class Adapter

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.

No alt text provided for this image

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.

要查看或添加评论,请登录

Matija Katadzic的更多文章

  • Send SMS messages with C# & .net6

    Send SMS messages with C# & .net6

    In modern web applications, the business may require to send SMS to end-users in various scopes like Alerting the…

  • Run and manage periodic background tasks in ASP.NET Core 6 with C#

    Run and manage periodic background tasks in ASP.NET Core 6 with C#

    Sometimes your web app needs to do work in the background periodically e.g.

  • Mapping Experiment in .net core- AutoMapper, ExpressMapper, Mapster & Manual mapping

    Mapping Experiment in .net core- AutoMapper, ExpressMapper, Mapster & Manual mapping

    Nuget package that we need: You can read more in official pages: AutoMapper - https://automapper.org/, BenchmarkDotNet…

  • What is a Decorator Pattern?

    What is a Decorator Pattern?

    The decorator pattern (also known as Wrapper) is a structural design pattern and it allows developers to dynamically…

  • What is Strategy Design Pattern?

    What is Strategy Design Pattern?

    Let’s say you are implementing an online eCommerce shop checkout page and you have been told to implement a feature…

  • What is a Builder Pattern?

    What is a Builder Pattern?

    Builder Design Pattern allows us to separate the construction of a complex object from its representation so that we…

  • What is Observer Design Pattern?

    What is Observer Design Pattern?

    The Observer is a behavioral design pattern and it allows some objects (known as subjects or publishers or observables)…

  • What’s a design pattern?

    What’s a design pattern?

    Design patterns are typical solutions to commonly occurring problems in software design. They are like pre-made…

  • What is gRPC?

    What is gRPC?

    What is gRPC? gRPC (gRPC Remote Procedure Calls) is an open source remote procedure call (RPC) system initially…

社区洞察

其他会员也浏览了