Adapter Design Pattern
Adapter design pattern is a structural pattern.Adapter pattern lets two components work together which have incompatible interfaces.It is useful if we need to implement some functionality in our application and a class already exists which provides the required functionality.But The existing class providing the required functionality has a different interface than the one expected by the client.Here we will understand use and example of Adapter Design Pattern in C#.
In this case modifying the source code of either the client or the class providing the desired functionality is not a good option.So we can create and use an adapter which will serve as an interface between the client class and the service class.
We can describe the Adapter Pattern with the following UML diagram
The 4 main components in this pattern are
- Client Class which consumes the functionality defined by the Adaptee
- ITarget Interface which the client uses
- Adapter Class which converts or Adapts the ITarget interface to the Adaptee
- Adaptee Class which provides the functionality required by the client.
Adapter is an adapter that converts an existing interface into one required by the client.Adapter contains a reference to the Adaptee.When client calls the Operation() method defined by the adapter,Adapter calls the OriginalOperation() which is defined by the Adaptee.
Like a real world adapter it allows different components to work together.A common example is usb cable.We can not directly plugin the usb cable into the power socket.But using an adapter we can plugin the usb cable into the power socket.
By using the adapter client is able to use the existing functionality provided by the Adaptee class.
Example of Adapter pattern
Our application defines a Student class:
class Student:ILogger { public string Name { get; set; } public string LogPath { get { return ConfigurationManager.AppSettings["loggingPath"]; } } private Logger _logger; public Logger Logger { get { if(_logger==null) { _logger = new Logger(); } return _logger; } } public Student(string name) { Name = name; LoggingInfo(String.Format("Creating student with name {0}", name), LogPath); } public void LoggingInfo(string message, string filePath) { Logger.Log(message,filePath); } } interface ILogger { void LoggingInfo(string message,string filePath); }
For the logging functionality there is an existing class Logger.The Logger class is defined in an external assembly
public class Logger { public void Log(string filePath,string message) { using (StreamWriter file =new StreamWriter(filePath, true)) { file.WriteLine(message); } } }
Now in some client application there is a problem in finding logged details as thousands of messages are being logged everyday.
Because of this the interface of this class changes and instead of just passing the message to log it also requires the date and time details as well.But our application is already calling the Log method in lots of places.
We do not have access to the source code of the Logger class as it is defined in an external assembly.So this will require changes at many places in the application.Also application will need to be thoroughly tested after these changes.
To prevent this problem we can call the log method of the logger class through an adapter rather than directly calling the LoggingInfo() method.
So we define the adapter class as:
public class LoggerAdapter:ILogger { const string LogPath=ConfigurationManager.AppSettings["MessageLoggingPath"]; private Logger _logger; public Logger Logger { get { if (_logger == null) { _logger = new Logger(); } return _logger; } } public void LoggingInfo(string message) { LoggingInfo(message, LogPath); } public void LoggingInfo(string message, string filePath) { Logger.Log(message, filePath); } }
This LoggerAdapter class implements the functionality to log information using the Logger class.This decouples other classes from the Logger class.So changes in the Logger class does not impact all the classes in our application.It is only the adapter class that is impacted by the changes in the Logger class.
So our Student class simplifies to
class Student { public string Name { get; set; } private LoggerAdapter _logger; public LoggerAdapter Logger { get { if (_logger == null) _logger = new LoggerAdapter(); return _logger; } } public Student(string name) { Name = name; Logger.LoggingInfo(String.Format("Creating student with name {0}", name)); } }
With the new requirement to add the date time details to the logged information ILogger is modified to pass the datetime parameter in the LoggingInfo() method.
interface ILogger { void LoggingInfo(string message,string filePath,DateTime current); }
So to pass the date information to the LoggingInfo() method we only need to modify the method in the adapter class:
public void LoggingInfo(string message) { LoggingInfo(message, LogPath,DateTime.Now); } public void LoggingInfo(string message, string filePath,DateTime currentDateTime) { Logger.Log(message, filePath,currentDateTime); }