Adapter Design Pattern In C#
Contents
- What is Adapter Design Pattern?
- Adapter Pattern Structure
- Adpater Pattern Real World Example
- Where To Apply Adapter Pattern?
- Further Reading
What is Adapter Design Pattern?
The Adapter Pattern is a software design pattern that attempts to reconcile the differences between two otherwise-incompatible interfaces. This pattern is especially useful when attempting to adapt to an interface that cannot be refactored.
When you have a class that needs to utilize a particular interface, and you have a library that includes the functionality you need, but it doesn’t use the interface that you require. You can achieve the reuse of that library’s code by creating an Adapter class. This adapter class sits between your client code, and the code that’s in this library, and adapts one interface to the other. The Adapter design pattern is one of the most common, and most useful patterns available to us as software developers.
Adapter Pattern Structure
Let’s look at the structure of the Adapter Pattern using this UML diagram. The two basic players within this example are the Client, and the Adaptee, shown above.
Now,
The Client needs some of the logic that exists within the Adaptee. Specifically, there is this AdaptedOperation that has the code that the Client wants to be able to utilize. Unfortunately, the Client has been written in such a way that it cannot directly call this AdaptedOperation because its interface is not the one that the client expects. This is where the Adapter Pattern comes into play.
First,
The Adapter interface is created, exposing an operation that has the interface the client expects.
Next,
For each different implementation required, at a minimum, one, its different ConcreteAdapter is created that takes that Operation and implements it, such that that code calls the AdaptedOperation. In this way, the Client will now be able to call the Operation on the ConcreteAdapter, which in turn will call the AdaptedOperation on the Adaptee.
The Client really wants to use the Adaptee directly, but unfortunately, it can’t due to the incompatible interface. The Adapter Pattern is simply allowing us to achieve this despite this incompatibility.
Adpater Pattern Real World Example
Let’s see the implementation of Adapter pattern in C#, with IDbDataAdapter example,
IDbDataAdapter is one of the .Net built-in interfaces under System.Data
namespace.
The IDbDataAdapter interface inherits from the IDataAdapter interface and allows an object to create a DataAdapter designed for use with a relational database.
Now, In the below code, I created a DataRenderer
class that takes IDbDataAdapter
as a parameter and renders data that comes from data adapters in the form of data Tables.
Now consider,
If we want to render the below persons
list using the above DataRenderer
class, in the same format without tweaking its code.
But, DataRenderer
accepts an IDbDataAdapter
and thus it is incompatible with persons
datatype. However, one thing we can do is that we can create another renderer that accepts the persons
list.
What if,
We could convert this List<Person> persons
into the format that is compatible with DataRenderer
class then we don’t have to write the same repeatable code for rendering data. Let’s see how
After creating DbAdapter for our persons
list we can render our list using the existing DataRenderer
class. Let’s see how
Now from the above code, we can conclude that the List<Person>
is an Adaptee, DataRenderer
is a Client that depends on IDbDataAdapter
our Adapter and PersonCollectionDbAdapter
is our Concrete Adapter.
Thus, by working through an adapter, our client could reuse the existing object that provides the needed functionality.
If you yourself are writing a library or a framework, and you want to ensure that it’s useable by future classes that may not even have been written yet, and so you cannot be certain what their interface will be, you can add support for an Adapter as part of your interface for your code, and this will make it easier for other future applications to use your code.
This idea is used within the. NET Framework Library itself, you will find if you look at ADO.NET in the System.Data
namespace using a tool such as Reflector, that IDbDataAdapter has several derived types, including a concrete class called DbDataAdapter, also you’ll find the OdbcDataAdapter, OleDbDataAdapter, OracleDataAdapter, and SqlDataAdapter. Each of these implements at its core the IDbDataAdapter interface.
Where To Apply Adapter Pattern?
-
You should consider using the Adapter Pattern whenever you want to use an existing class’s functionality, but its interface is not the one that you require.
-
Another scenario, if you’re trying to create reusable code, and you don’t want to tie it too tightly to a particular implementation, you should use some kind of an Adapter interface as what you’re code depends on, so that future clients could implement their own version of that Adapter and still make use of your code.
-
You should remember the Open/Closed Principle, which states that modules should be open to extension but closed to modification, and by utilizing the Adapter Pattern in your implementations of your code, you allow for your code to better follow the Open/Closed Principle.
Note: You can download the complete solution demo from my github repository.
Further Reading
-
A simple example of the Open/Closed Principle by Joel Abrahamsson - The Open/Closed principle says that we should strive to write code that doesn’t have to be changed every time the requirements change. Here’s a simple example by Joel.
-
Populating a DataSet from a DataAdapter - Microsoft Docs - The ADO.NET DataSet is a memory-resident representation of data that provides a consistent relational programming model independent of the data source. The DataSet represents a complete set of data that includes tables, constraints, and relationships among the tables. Interaction with existing data sources is controlled through the DataAdapter.
Subscribe to Code with Shadman
Get the latest posts delivered right to your inbox