Adapter

Convert the interface of a class into another interface clients expect. Adapter lets classes work together that couldn't otherwise because of incompatible interfaces.

Introduction

  • Adapter (wrapper) is a design pattern belonging to the Structural Pattern group - design patterns for structural design

  • A design pattern that converts the interface of a class into another template that the client wants. Allows two unrelated templates to work together.

  • The Adapter Pattern plays an intermediary role between two classes, converting the interface of one or more existing classes into another interface, appropriate for the class being written. This allows classes with different interfaces to easily communicate with each other through an intermediate interface, without needing to change the code of the existing class or the class being written.

  • Adapter Pattern is also called Wrapper Pattern because it provides a compatible "outer" interface for an existing system, has appropriate data and behavior but has an interface that is incompatible with the class being written.

Purpose is born

Let's imagine that we are creating an application that tracks the stock market. The application downloads warehouse data from multiple sources in XML format and then displays beautiful charts and diagrams to the user.

At some point, we decided to improve the application by integrating a 3rd party smart analytics library. But there is a catch: the analytics library only works with data in JSON format.We can change the library to work with XML. However, this may impact existing code, making this approach impossible.

We can solve this problem by creating Adapters to convert XML to JSON format for each class of the analytics library. Then we adapt our code to only communicate with the library through these Adapters. When an Adapter receives a request, it translates the XML data to JSON and passes the request to the appropriate methods of the parsing object in the library.Adapters can not only convert data into many different formats but can also help objects with different interfaces collaborate with each other.

Here's how it works:

  • The adapter has an interface compatible with one of the existing objects.

  • By using this interface, the existing object can safely call the Adapter's methods.

  • When called, the Adapter passes the request to the second object, but in a format and order expected by the second object.

Architecture

There are two ways to implement the Adapter Pattern based on their implementation:

Object Adapter – Composition

In this model, a new class (Adapter) will refer to one (or more) objects of an existing class with an incompatible interface (Adaptee/Service), and implement the interface that the user desires (Target). . In this new class, when implementing methods of the desired user interface, the necessary method will be called through the object of the class with the incompatible interface.

Components in the model:

  • Client is a class that contains the business logic of the program

  • The client interface describes a protocol that other classes must follow to be able to collaborate with client code

  • Service: is a useful class (usually 3rd party or legacy). Clients cannot use this class directly because it has an incompatible interface.

  • Adapter: is a class that can work with both client and service: it implements the client interface, while encapsulating the service object. The Adapter when called from the Client through the Adapter Interface converts them into wrapped service object calls in a format it can understand.

Class Adapter – Inheritance

In this model, a new class (Adapter) will inherit an existing class with an incompatible interface (Adaptee/Service), and implement the interface that the user desires (Target). In the new class, when implementing the methods of the desired user interface, this method will call the necessary methods that it inherits from the class with the incompatible interface.

The components:

  • Class Adapter: no need to wrap any object because it inherits behavior from client and service. Adaptation occurs in overridden methods. The result of Adapter can be used in place of an existing client class

Compare Class Adapter and Object Adapter

  • The main difference is that Class Adapter uses Inheritance to connect Adapter and Adaptee while Object Adapter uses Composition to connect Adapter and Adaptee.

  • In the Class Adapter approach, if an Adaptee is a class and not an interface then Adapter will be a subclass of Adaptee. Therefore, it will not serve all other subclasses in the same way because Adapter is a specific subclass of Adaptee.

  • Object Adapter is better because it uses Composition to hold an instance of Adaptee, allowing one Adapter to work with multiple Adaptees if necessary.

Advantages & disadvantages

Advantage

  • Single Responsibility Principle: Can separate interface or data conversion code from the main business logic of the program

  • Open/Closed Principle: Helps code not be affected by changes or new version updates from APIs or services from third parties (changes in function names, class names,...)

Disadvantages

  • The overall complexity of the code increases because you need to introduce a new set of Templates and classes. Sometimes it's simpler to change the service layer to match the rest of your code.

Applicability

  • Use the Adapter class when you want to use some existing class, but its interface isn’t compatible with the rest of your code.

    • The Adapter pattern lets you create a middle-layer class that serves as a translator between your code and a legacy class, a 3rd-party class or any other class with a weird interface.

  • Use the pattern when you want to reuse several existing subclasses that lack some common functionality that can’t be added to the superclass.

    • You could extend each subclass and put the missing functionality into new child classes. However, you’ll need to duplicate the code across all of these new classes, which smells really bad.

The much more elegant solution would be to put the missing functionality into an adapter class. Then you would wrap objects with missing features inside the adapter, gaining needed features dynamically. For this to work, the target classes must have a common interface, and the adapter’s field should follow that interface. This approach looks very similar to the Decorator pattern.

How to Implement

  • Make sure that you have at least two classes with incompatible interfaces:

    • A useful service class, which you can’t change (often 3rd-party, legacy or with lots of existing dependencies).

    • One or several client classes that would benefit from using the service class.

  • Declare the client interface and describe how clients communicate with the service.

  • Create the adapter class and make it follow the client interface. Leave all the methods empty for now.

  • Add a field to the adapter class to store a reference to the service object. The common practice is to initialize this field via the constructor, but sometimes it’s more convenient to pass it to the adapter when calling its methods.

  • One by one, implement all methods of the client interface in the adapter class. The adapter should delegate most of the real work to the service object, handling only the interface or data format conversion.

  • Clients should use the adapter via the client interface. This will let you change or extend the adapters without affecting the client code.

When to use it

Adapters are used when:

  • Want to use some available classes but their interface is not compatible with current code

  • Want to reuse some existing subclass that lacks some functionality and cannot be added to the parent class Adapter is often used in programming environments where new components or new applications need to be integrated and work together with other subclasses. existing ingredients.

Relations with Other Patterns

  • Bridge is usually designed up-front, letting you develop parts of an application independently of each other. On the other hand, Adapter is commonly used with an existing app to make some otherwise-incompatible classes work together nicely.

  • Adapter provides a completely different interface for accessing an existing object. On the other hand, with the Decorator pattern the interface either stays the same or gets extended. In addition, Decorator supports recursive composition, which isn’t possible when you use Adapter.

  • With Adapter you access an existing object via different interface. With Proxy, the interface stays the same. With Decorator you access the object via an enhanced interface.

  • Facade defines a new interface for existing objects, whereas Adapter tries to make the existing interface usable. Adapter usually wraps just one object, while Facade works with an entire subsystem of objects.

  • 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 your code in a specific way. It can also communicate to other developers the problem the pattern solves.

Last updated