Adapter Pattern in C#

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.

No alt text provided for this image

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.

No alt text provided for this image

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:

  • Client: The Client is a class containing the program's existing business logic. The client code doesn't get coupled to the concrete adapter class as long as it works with the adapter via the client interface. Thanks to this, we can introduce new types of adapters into the program without breaking the existing client code. This can be used when the interface of the service class gets changed or replaced. We can just create a new adapter class without changing the client code.
  • Client Interface: The Client Interface describes the protocol that other classes must follow to collaborate with the client code.
  • Service: The Service is some class (usually 3rd-party or legacy). The client can't use this class directly because it has an incompatible interface.
  • Adapter: The Adapter is a class that works with both the client and the service: it implements the client interface while wrapping the service object. The adapter receives calls from the client via the adapter interface and translates them into calls to the wrapped service object in a format it can understand.

No alt text provided for this image

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.

No alt text provided for this image

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:

No alt text provided for this image

Now let's create a Meats class:

No alt text provided for this image

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:

No alt text provided for this image

Finally, in our Main() method, we can now show the difference between using the legacy class by itself and using the Adapter class:

No alt text provided for this image

The output of our application will be the following:

No alt text provided for this image

Pros and Cons of Adapter Pattern

No alt text provided for this image

Relations with Other Patterns

  • The Bridge is usually designed up-front, letting us develop parts of an application independently of each other. On the other hand, the Adapter is commonly used with an existing application to make some otherwise-incompatible classes work together nicely.
  • The Adapter changes the interface of an existing object, while the Decorator enhances an object without changing its interface. In addition, the Decorator supports recursive composition, which isn't possible when we use the Adapter.
  • The Adapter provides a different interface to the wrapped object, the Proxy provides it with the same interface, and the Decorator provides it with an enhanced interface.
  • Bridge, State, Strategy and to some degree Adapter have very similar structures. Indeed, all of these patterns are based on composition, which is delegating work to other objects. However, they all solve different problems. A pattern isn't just a recipe for structuring our code in a specific way.

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.

To view or add a comment, sign in

More articles by Konstantinos Kalafatis

  • Premises of Software Architecture

    This post is the first of a series of posts around Software Architecture. In this series we are going to discuss what…

  • Visitor Pattern in C#

    The Visitor is a behavioural design pattern that lets us separate algorithms from the entities on which they operate…

  • Observer Pattern in C#

    Originally posted here The Observer is a behavioural design pattern that lets us define a subscription mechanism to…

  • Mediator Pattern in C#

    Originally posted here The Mediator is a behavioural design pattern that lets us reduce chaotic dependencies between…

  • Factory Method Pattern in C#

    Originally posted here The Factory Method is a creational design pattern that allows the abstraction of object…

  • Singleton Pattern in C#

    Originally posted here The Singleton is a creational design pattern that lets us ensure that a class has only one…

  • Composite Pattern in C#

    Originally posted here The Composite is a structural design pattern that allows us to compose objects into tree…

  • Decorator Pattern in C#

    Originally posted here The Decorator is a structural design pattern that lets us attach new behaviours to objects by…

  • Builder Pattern In C#

    Originally posted here The Builder is a creational design pattern that allows us to construct complex objects step by…

    1 Comment
  • Application Caching Strategies

    Originally posted here Most, if not all, developers are at least somewhat familiar with the concept of caching. After…

Others also viewed

Explore content categories