Introducing Adapter Design Pattern

Tram Ho

Adapter

** Other name: ** Wrapper

Scheme

An adapter is a structural design pattern that allows interfaces that are not compatible with each other to interact with each other.

Problem

Imagine that you are creating an application that tracks the stock market. The application loads stock data from multiple sources in XML format and then displays the tabular diagrams presented to the user. At some point, you decide to improve the application by integrating a 3rd party intelligent analytics library. But there is one problem: This library only works with data in JSON format.

You cannot just insert the library because it requires data in a format that is not compatible with your application.

You can edit the library so it can process XML. However, this may break some existing code that is dependent on the library. And not to mention that you probably didn’t have access to the library’s source code in the first place, making this approach impossible to implement.

Solution

You can create an Adapter. This is a special object that converts the look and feel of one object so that another can understand it.

The adapter wraps one of the objects to hide the complicated transitions that take place behind the scenes. The wrapped object doesn’t even know about the adapter. For example, you could wrap an object operating in meters and kilometers in an adapter that converts all the data into feet and miles.

Adapters can not only convert data into many different formats, but also help objects with different interfaces to interact with each other. How it works together follows:

  1. The adapter gets an interface compatible with one of the existing objects.
  2. Using this interface, an existing object can securely call the adapter’s methods.
  3. When called, the adapter passes the request to the second object, but in the format and order that the second object expects. Sometimes, we can even create a bidirectional adapter to convert calls in both directions.

Let’s go back to our stock market application. To solve the problem of incompatible formats, you can create an XML to JSON adapter for every library class in which your code works directly. Then, you edit your code to only communicate with the library through these adapters. When an adapter receives a call, it translates the received XML data into a JSON structure and passes the call to the appropriate methods of a wrapped object.

Structure

Adapter Object This implementation uses the principle of object composition: Adapter implements the interface of one object and wraps the other. This can be written in all popular programming languages.

Structure of the DP adapter (adapter object)

  1. Client is a class containing the existing business logic of the program.
  2. The client interface describes a protocol that other classes must follow in order to interact with the client code.
  3. Service is some of the classes that we need to implement a certain item (usually 3rd party or legacy). Client cannot use this class directly because it has an incompatible interface.
  4. Adapter is a class that can operate on client and service: it implements the client interface and wraps the service object simultaneously. The adapter receives calls from the client through the adapter interface and converts them into calls to the service object wrapped in a format it can understand.
  5. The client code is not coupled to a specific adapter class, as long as it works with the adapter through the client interface. Thanks to that, you can introduce new types of adapters into the program without breaking the existing client code. This can be useful when the interface of the service class is changed or replaced, since you only have to create a new adapter class without changing the client code.

Class adapter This implementation uses inheritance: the adapter inherits interfaces from both objects at the same time. Note that this method can only be implemented in programming languages ​​that support multiple inheritance, such as C ++.

  1. The adapter class does not need to wrap any objects as it inherits the behavior from client and service. Modification occurs in override methods. The resulting adapter can be used in place of the existing client class.

Fake code

The following example of Adapter DP is built on the classic conflict relationship between “square lump” and “round hole”.

Adapter pretends to be a “circle”, with a radius equal to half the diameter of the square (in other words, the radius of the smallest circle can contain the square).

Applicability

  1. Use class adapter when you want to use some existing class, but its interface is not compatible with the rest of your code. The adapter pattern allows you to create an intermediate class that acts as a translator between your code and a derived class, a 3rd party class, or any other class that has a weird interface.
  2. Use this DP when you want to reuse some existing subclass that are missing some common functionality, but can’t be added to the parent class. You can extend each subclass and include missing functionality in new subclasses. However, you will need to duplicate code on all of these new classes, and this is very rotten code .

A much more elegant solution would be to put the missing functionality into an adapter class. Then, you’ll wrap the objects with the missing features inside the adapter, dynamically grabbing the required features. For this to work, the target classes must have a common interface, and the adapter fields must obey that interface. This approach is very similar to Decorator DP.

Implementation method

  1. Make sure you have at least two classes with incompatible interfaces: A service class that you cannot change (usually 3rd party legacy, or have a lot of dependencies) One or more client classes will enjoy Benefit from using service class.
  2. Declare the client interface and describe how the client communicates with the service.
  3. Create the adapter class and make it compliant with the client interface. In this step, temporarily leave all methods blank.
  4. Add a field to the adapter class to store a reference to the service object. It is common practice to instantiate this field through the constructor, but it is sometimes more convenient to pass it to the adapter when calling its methods.
  5. Implement all methods of the client interface in the adapter class. The adapter should delegate most of the practical work to the service object, and only handle interface conversion or data format.
  6. The client should use the adapter through the client interface. This will allow you to change or extend the adapter without affecting the client code.

Advantages and disadvantages

Advantages:

  1. Single Responsibility Principle. You can separate the interface or data transformation code from the main business logic of the program.
  2. Open / Closed Principle. You can introduce new types of adapters into the program without violating the existing client code, as long as they work with the adapter through the client interface.

Cons: The overall complexity of the code increases because you need to write a new set of interfaces and classes. Sometimes, you just need to simply change the service class so it works with the rest of your code.

Relationship with other design patterns

Bridges are often pre-designed, allowing you to develop parts of applications independently of each other. On the other hand, adapters are often used with an existing application to make several incompatible classes work with each other. The adapter changes the look and feel of an existing object, while the decorator upgrades an object without changing its appearance. Additionally, Decorator supports recursive composition, which is not possible when you are using the adapter. Adapter provides a different interface to the wrapped object, Proxy gives it the same interface, and Decorator gives it an enhanced interface. Facade defines a new interface for existing objects, while Adapter tries to make the existing interface usable. Adapters typically encapsulate only one object, whereas Facade works with the entire subsystem of objects. Bridge, State, Strategy (and to some extent Adapter ) are very similar in structure. In fact, all of these patterns are compos-based, that is, delegating work to other objects. However, they solve different problems. A DP is not only a formula for structuring your code, but also a way for you to communicate with other developers about a problem you want to solve using that DP.

Share the news now

Source : Viblo