Adapter Design Pattern

The Adapter design pattern works when you have different or incompatible interfaces, and you need the client to use both in the same way.

The Adapter design pattern says in its definition that it converts an interface or class into another interface that the client needs.

What are the elements of the Adapter design pattern?

The Adapter pattern parts are

  • Target: the interface we use to create the adapter.
  • Adapter: is the implementation of the target and will take care of the adaptation.
  • Client: it is the one that interacts with and uses the adapter.
  • Adaptee: it is the incompatible interface that we need to adapt with the adapter.

Adapter Design Patter

How to create the Adapter design pattern with Java?

We mentioned earlier that the pattern had several parts. A client that interacts with the Adapter to get what it needs. The Target from which the Adapter will be implemented, who will be in charge of adapting the Adaptee response so that the Client understands it. The Adaptee that represents the class with an incompatible interface for the client

Adapter Design Patter

Let’s take this to an example where a Client service needs information from a BankService service that returns the information in the BankData class.

  1. Client: the client service needs information from a bank account and needs it in the User class.
  2. Client: the client calls the adapter to get information from the User.
  3. Adapter: the adapter calls the service to return information.
  4. Adaptee: the service returns the BankUser, the Adaptee.
  5. Adapter: the adapter converts BankData to User and returns it to the client.
  6. Client: the client receives the info in User. The class User that the client can understand.

adapter-bankdata-to-user.png

Let’s see the classes:

The service that returns the BankData


package patterns.adapter;

public class BankService {

    public BankData findByAccountNumber(int accountNumber) {
        if (accountNumber == 1) {
            return new BankData(accountNumber,"Nick", 100d);
        } else if (accountNumber == 3) {
            return new BankData(accountNumber,"Susan", 200d);
        } else {
            return null;
        }
    }
}

The BankData class with the info


package patterns.adapter;

public class BankData {

    private int account;
    private String name;
    private double balance;

    public BankData(int account, String name, double balance) {
        this.account = account;
        this.name = name;
        this.balance = balance;
    }

    public String getName() {
        return name;
    }

    public int getAccount() {
        return account;
    }

    public double getBalance() {
        return balance;
    }
}

The User class required by the client


package patterns.adapter;

public class User {

    private int id;
    private String name;
    private double totalAvailable;

    public User(int id, String name, double totalAvailable) {
        this.id = id;
        this.name = name;
        this.totalAvailable = totalAvailable;
    }

    public int getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public double getTotalAvailable() {
        return totalAvailable;
    }
}

The target (interface) from which we created the Adapter

package patterns.adapter;

public interface UserAdapter {

    User getUser(int accountNumber);

}

The implementation of the adapter

package patterns.adapter;

public class UserAdapterImpl implements UserAdapter {

    private BankService service = new BankService();

    @Override
    public User getUser(int accountNumber) {
        BankData bankData = service.findByAccountNumber(accountNumber);
        User user = new User(bankData.getAccount(), bankData.getName(), bankData.getBalance());
        return user;
    }
}

The client that needs information in the User class

package patterns.adapter;

public class Client {

    public static void main(String[] args) {

        UserAdapterImpl adapter = new UserAdapterImpl();
        adapter.getUser(1);

    }
}

Conclusion

We have seen that the Adapter pattern resolves incompatibilities between interfaces by adapting one interface to the other using an adapter. The adapter is responsible for the conversion between interfaces.

This is a widely used pattern, especially when we communicate with external apis and need to adapt its response to our interfaces.

You can review this code GitHub

Hi! If you find my posts helpful, please support me by inviting me for a coffee :)

See also