Adapter Pattern and Java example

This is a pattern which helps you put a square peg in a round hole. Sound impossible? Not when we have Adapter.

The Adapter Pattern converts the interface of a class into another interface the clients expect.

In fact, Adapter all around us!!! You'll have no trouble understanding what an OO adapter is because the real world is full of them. This is an example:

adapter-Example.webp

So if it walks like a duck and quacks like a duck, then it might not be a duck but a turkey wrapped with a duck adapter.

Coding

It's time to see an adapter in action. I will create a simple interface of the Duck.

public interface Duck {
    public void quack();

    public void fly();
}

And here's a subclass of Duck, the VietnamDuck.

public class VietnamDuck implements Duck {
    public void quack() {
        System.out.println("Quack");
    }

    public void fly() {
        System.out.println("I'm flying");
    }
}

I create a new animal: WildTurkey

public interface Turkey {
    public void gobble();

    public void fly();
}

public class WildTurkey implements Turkey {
    public void gobble() {
        System.out.println("Gobble");
    }

    public void fly() {
        System.out.println("I'm flying a short distance");
    }
}

Now we have some Duck objects and you'd like to use some Turkey objects in their places. Obviously we can't use the turkeys outright because they have a different interface.

So let's write an Adapter

// First, you need to implement the interface of the type you're adapting to.
// This is the interface your client expects to see.
public class TurkeyAdapter implements Duck {
    Turkey turkey;

    // we need to get a reference to the object that we are adapting
    // here we do that through the constructor
    public TurkeyAdapter(Turkey turkey) {
        this.turkey = turkey;
    }

    // now we need to implement all the methods in the interface;
    // turkey can't do long-distance flying like ducks, so we call 5 times
    @Override
    public void fly() {
        for (int i = 0; i < 5; i++) {
            turkey.fly();
        }
    }

    // translate from quack to gobble method
    @Override
    public void quack() {
        turkey.gobble();
    }
}

Test the adapter

Now we just need some code to test drive our adapter:

public class DuckTest {

    // this is a client is implemented against the target interface
    static void testDuck(Duck duck) {
        duck.quack();
        duck.fly();
    }

    public static void main(String[] args) {

        // let's create a Duck and a Turkey
        VietnamDuck duck = new VietnamDuck();
        WildTurkey turkey = new WildTurkey();

        // And then wrap the turkey in a TurkeyAdapter, which makes it look like a Duck
        Duck turkeyAdapter = new TurkeyAdapter(turkey);

        System.out.println("The Turkey says ...");
        turkey.gobble();
        turkey.fly();

        // using testDuck method which expects a Duck object input
        System.out.println("\nThe Duck says ...");
        testDuck(duck);

        // now we try to pass off the turkey as a duck...
        System.out.println("\nThe TurkeyAdapter says ...");
        testDuck(turkeyAdapter);

    }
}

Let's run it and see...

Explain

I could explain about how the Client uses the Adapter

  1. The client makes a request to the adapter by calling a method on it using the target interface.
  2. The adapter translates the request into one or more calls on the adaptee using the adaptee interface.

    • The Adapter implements the target interface and holds an instance of the Adaptee.

      Eg: TurkeyAdapter implemented the target interface, Duck. And Turkey was the adaptee interface.

  3. The client receives the results of the call and never knows there is an adapter doing the translation.

Summary

Adapter makes your software more flexible.

Now despite having defined the pattern, we haven't told you the whole story yet. There are actually two kinds of adapters: object adapters and class adapters. But I will cover it in another post :)))

See yahh!!!