Adapter Design Pattern Tutorial In Java

Introduction

The Adapter design pattern is a structural pattern that allows objects with incompatible interfaces to work together. It does this by converting the interface of one class into another interface that the client expects. This pattern is often used to make existing classes work with each other without modifying their source code.

Real-world example

Consider the mundane task of pouring water from a can into a bottle. You would have no problem if the can had a straw like this:

Compatible interfaces

However, in case you have a big can and a small bottle, you probably not be able to complete the task:

Incompatible interfaces
Incompatible interfaces

In this case, what would you do? You would get a funnel to get the job done.

Using an adapter to make incompatible interfaces work with each other
Using an adapter to make incompatible interfaces work with each other

That is an example of the adapter pattern in real life. And yes, we are not using the wall socket example!

Code Examples

Imagine you have an iPhone (in 2022, all iPhones still use the lightning charger) and go to a party without bringing your phone charger. You ask around and no one has an iPhone charger. All people use type-c chargers.

You are now desperate because the battery is almost dead and you can’t live without your iPhone (you can’t live a single minute without scrolling social media sites up and down).

Luckily, someone comes and gives you an adapter. On one end, it has a female type-c port. On the other end, it has a male lightning plug.

Which is exactly like this:

a type-c to lightning adapter
a type-c to lightning adapter

You thank the gentleman for providing life support and use the adapter right away to start charging your iPhone.

Now let’s see how we can do the same thing with code. Let’s first consider the diagram of interfaces:

The adapter pattern diagram

Here, you can see that we have two types of chargers: TypeCCharger and LightningCharger. They both implement the Charger interface which has only one abstract method: transferElectricity.

The IPhone class only accepts a LightningCharger. Thus, the type-c charger would not work. However, we created an adapter class that implements the LightningCharger interface. This guarantees the iPhone could use instances of this class.

In addition, the adapter class takes the TypeC charger as its constructor’s parameter, and in its transferElectricity method, applied the logic to get the energy from the type-c charger to transfer to the iPhone.

Here is the code:

package com.datmt.java_core.dp;

public class AdapterPattern {
    public static void main(String[] args) {
        var iphone = new IPhone();

        var typeCCharger = new TypeCChargerImpl();
        var adapter = new LightningTypeCChargerAdapter(typeCCharger);

        iphone.charge(adapter);

    }
}

interface Charger {
    void transferElectricity();
}
interface LightningCharger extends Charger {


}
class LightningChargerImpl implements LightningCharger {

    @Override
    public void transferElectricity() {

    }
}

interface TypeCCharger extends Charger {

}

class TypeCChargerImpl implements TypeCCharger {
    @Override
    public void transferElectricity() {

    }
}

class LightningTypeCChargerAdapter implements LightningCharger {
    private TypeCCharger typeCCharger;

    public LightningTypeCChargerAdapter(TypeCCharger typeCCharger) {
        this.typeCCharger = typeCCharger;
    }

    @Override
    public void transferElectricity() {
       this.typeCCharger.transferElectricity();
    }
}


class IPhone {
   void charge(LightningCharger charger) {
       //charge
   }
}

Conclusion

The Adapter pattern enables classes with incompatible interfaces to interact with each other. It does this by introducing a new class, the Adapter class, to convert the foreign interface (type-c charger) into an interface similar (the lightning charger) to the requested class (iPhone).

Leave a Comment