Adapter Pattern in C#
Originally posted here
The Adapter is a structural design pattern that allows objects with incompatible interfaces to collaborate.
The Adapter attempts to reconcile the differences between two otherwise incompatible interfaces or classes. In doing so, it wraps one of the classes in a layer that allows it to talk to the other.
You can find the example code of this post, on GitHub.
Conceptualizing the Problem
Imagine that we're creating a stock market monitoring app. The app downloads the financial data from multiple endpoints in XML format processes them, and then displays them in pretty-looking charts and diagrams to the end user.
At some point, we decided to improve the app by integrating a 3rd-party visualization library. But there's a catch: the visualization library only works with data in JSON format.
We could change the library to work with XML. However, this might break some existing code that relies on the library. Moreover, while the library is open-source, getting a copy and changing the library's code to support XML is an exercise in futility (I have seen this practice more than once though...)
What we can do, is create an adapter. An adapter is a special object that converts the interface of one object so that another object can communicate with it.
The adapter wraps one of the objects to hide the complexity of conversion happening under the hood. The wrapped object isn't even aware of the adapter. For example, we can wrap an object that operates in grams and celsius degrees with an adapter that converts all of the data to units such as pounds and Fahrenheit degrees.
Adapters can not only convert data into various formats but can also help objects with different interfaces collaborate.
Let's get back to our stock market app. To solve the problem of incompatible formats, we can create an XML-to-JSON adapter for every class of the analytics library that our code works with directly. Then we have to adjust our code to communicate with the library only via these adapters. When an adapter receives a call, it translates the incoming XML data into a JSON object and passes the call to the appropriate methods of a wrapped analytics object.
Structuring the Adapter Pattern
The adapter pattern comes in two flavours: the Object adapter and the Class adapter.
Object adapter
This implementation of the adapter pattern uses the object composition principle: the adapter implements the interface on one object and wraps the other.
In this implementation, the Object Adapter has four participants:
Recommended by LinkedIn
Class Adapter
This implementation uses inheritance: the adapter inherits interfaces from both objects at the same time. Note that this approach can only be implemented in languages that support multiple inheritance.
In this implementation, the Class Adapter doesn't wrap any object. It inherits the behaviours from both the client and the service, and the adaptation happens within the overridden methods. The resulting adapter can be used in place of an existing client class.
To demonstrate how the Adapter pattern works, we will create a meat-safe-cooking temperature database.
For this example, we have an old, legacy system which stores the temperature data. This legacy system will be represented by the MeatsDatabase and will be our Service participant. Such a system might look like the following:
Now let's create a Meats class:
The problem is that we cannot modify the legacy API, which is the MeatDatabase class. Here's where our Adapter participant comes into play: we need another class that inherits from Meat but maintains a reference to the API such that the API's data can be loaded into an instance of the Meat class:
Finally, in our Main() method, we can now show the difference between using the legacy class by itself and using the Adapter class:
The output of our application will be the following:
Pros and Cons of Adapter Pattern
Relations with Other Patterns
Final Thoughts
In this article, we have discussed what is the Adapter pattern, when to use it and what are the pros and cons of using this design pattern. We then examined what is a class adapter and what is an object adapter and how the Adapter pattern relates to other classic design patterns.
It's worth noting that the Adapter pattern, along with the rest of the design patterns presented by the Gang of Four, is not a panacea or a be-all-end-all solution when designing an application. Once again it's up to the engineers to consider when to use a specific pattern. After all these patterns are useful when used as a precision tool, not a sledgehammer.