Burak Aktas Software Engineer

Adapter Design Pattern in Java

Adapter design pattern is a structural design pattern that provides two unrelated interfaces to work together. The most common illustration about adapter design pattern is sockets from different countries. Well most of us faced with laptop adapter issues when traveling, and looked for appropriate adapter for our laptops. So, by using an adapter we are able to plug our laptop chargers to sockets. Or using a card reader to plug our memory card from our camera to our computer. Thus, we make two unrelated interfaces to work together. The elements of adapter design pattern are Client, Target, Adapter and Adaptee. We can see the Gang of Four definition for ADP, and description of each element of it below;

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

Elements

  • Client: collaborates with objects adaptable to the Target interface.
  • Target: defines the domain-specific interface that Client uses.
  • Adapter: adapts the interface of Adaptee to the Target interface.
  • Adaptee: defines an existing interface that needs adapting.

Types

  • Object ADP: Uses (wraps) an instance of the class it wants to adapt.
  • Class ADP: Inherits from the class it wants to adapt.

Here is the actual UML diagram for ADP

Now let's implement an example of ADP. Suppose that we have implemented a RPG (role playing game) application by using a third party API that has only Fighter interface.

Fighter.class

public interface Fighter {
    public void attack();
    public void defend();
    public void escape();
}

And a basic implementation of our Fighter interface.

Knight.class

public class Knight implements Fighter {

    @Override
    public void attack() {
        System.out.println("Knight attacks!!");
    }

    @Override
    public void defend() {
        System.out.println("Knight defends...");
    }

    @Override
    public void escape() {
        System.out.println("Run Knight run...");
    }
}

After a while, a Wizard interface/class has published but we still want to use it again as a Fighter. So, what we have to do is adapting this Wizard class (adaptee) into a Fighter (Target) via an adapter class because Wizard has different functionality than Fighter. Here is the implementation of Wizard class;

Wizard.java

public class Wizard {

    public void castDestructionSpell() {
        System.out.println("Fireball is coming!!!");
    }

    public void shield() {
        System.out.println("Wizard casted shild spell...");
    }

    public void openPortal() {
        System.out.println("No need to run lets open a portal");
    }
}

So, our WizardAdapter class will implement Fighter interface and call appropriate functions of Wizard class.

WizardAdapter.java

public class WizardAdapter implements Fighter {

    private Wizard wizard;

    public WizardAdapter(Wizard wizard) {
        this.wizard = wizard;
    }

    @Override
    public void attack() {
        this.wizard.castDestructionSpell();
    }

    @Override
    public void defend() {
        this.wizard.shield();
    }

    @Override
    public void escape() {
        this.wizard.openPortal();
    }
}

And finally our Client is ready to use adapted Wizard class.

public class Main {

    public static void main(String args[]) {
        Fighter barbarian = new Barbarian();
        Wizard wizard = new Wizard();
        WizardAdapter wizardAdapter = new WizardAdapter(wizard);

        System.out.println("-----Barbarian's Action------");
        barbarian.attack();
        barbarian.defend();
        barbarian.escape();

        System.out.println("\n-----Wizard's Action------");
        wizardAdapter.attack();
        wizardAdapter.defend();
        wizardAdapter.escape();
    }
}

This is how we used the adapter design pattern by our example.

Advanced Example

We saw the simplest implementation of ADP, however, due to the reason that we are dealing with huge systems we may need many adapters for different kind of adaptees. Consider that we have a system which provides paying customers bills, calculating their balance, credit history etc. through external APIs of some banks and other companies. And these kind of operations will be made by sending requests to their systems. So, we have an Accounting adapter for Banks (Bank of America and HSBC) and Credit History Service adapter for a company called CompX. We can see the UML diagram for this system below.

Actually, there is no need to show adaptee objects. Each adapter contains an appropriate adaptee object and sends a http request to process it to the related systems. This is where adaptors providing to deal with unrelated interfaces.