Java Adapter Pattern example

16 April 2014
By Gonçalo Marques
In this tutorial we will see how to implement the Adapter design pattern in Java.

Adapter Design Pattern

The Adapter Design Pattern is a structural pattern that, as the name states, provides a way of adapting an existing interface into another one that is expected instead. This pattern provides a way of eventual reuse of already existing interface implementations by clients that expect a different interface (or interfaces that provide similar behaviour under a slightly different contract).

The interface that will be adapted into another interface is often called the adaptee.

We will see an example in the next sections in order to better illustrate the pattern.

Pattern demonstration

We start by defining a simple interface that represents a rectangle:

Rectangle.java

package com.byteslounge.shape;

public interface Rectangle {

  int getTopLeftX();

  int getTopLeftY();

  int getWidth();

  int getHeight();

}

And a client method that expects a Rectangle instance to be passed in:

ShapeUtils.java

package com.byteslounge.util;

import com.byteslounge.shape.Rectangle;

public class ShapeUtils {

  public static void drawRectangle(Rectangle rectangle) {
    System.out.println("Drawing rectangle [topLeftX: "
        + rectangle.getTopLeftX() + ", topLeftY: "
        + rectangle.getTopLeftY() + ", width: " + rectangle.getWidth()
        + ", height: " + rectangle.getHeight() + "]");
  }

}

This illustrative client is some kind of shape drawer which drawRectangle() method is expecting to receive a Rectangle instance.

Suppose that we already have a rectangle implementation, which contract is different from the one that the drawer (client) is expecting:

OtherRectangle.java

package com.byteslounge.geometry;

public interface OtherRectangle {

  int getTopLeftX();

  int getTopLeftY();

  int getBottomRightX();

  int getBottomRightY();

}

And the corresponding implementation:

SomeRectangle.java

package com.byteslounge.geometry;

public class SomeRectangle implements OtherRectangle {

  private final int topLeftX;
  private final int topLeftY;
  private final int bottomRightX;
  private final int bottomRightY;

  public SomeRectangle(int topLeftX, int topLeftY, int bottomRightX,
      int bottomRightY) {
    this.topLeftX = topLeftX;
    this.topLeftY = topLeftY;
    this.bottomRightX = bottomRightX;
    this.bottomRightY = bottomRightY;
  }

  @Override
  public int getTopLeftX() {
    return topLeftX;
  }

  @Override
  public int getTopLeftY() {
    return topLeftY;
  }

  @Override
  public int getBottomRightX() {
    return bottomRightX;
  }

  @Override
  public int getBottomRightY() {
    return bottomRightY;
  }

}


We may write an adapter that will make our already existing rectangle interface to work with the interface that is expected by the client:

RectangleAdapter.java

package com.byteslounge.shape.adapter;

import com.byteslounge.geometry.OtherRectangle;
import com.byteslounge.shape.Rectangle;

public class RectangleAdapter implements Rectangle {

  private final OtherRectangle rectangle;

  public RectangleAdapter(OtherRectangle rectangle) {
    this.rectangle = rectangle;
  }

  @Override
  public int getTopLeftX() {
    return rectangle.getTopLeftX();
  }

  @Override
  public int getTopLeftY() {
    return rectangle.getTopLeftY();
  }

  @Override
  public int getWidth() {
    return rectangle.getBottomRightX() - rectangle.getTopLeftX();
  }

  @Override
  public int getHeight() {
    return rectangle.getBottomRightY() - rectangle.getTopLeftY();
  }

}

The adapter must obviously implement the interface that is expected by the client: Rectangle. Then it holds a reference to the OtherRectangle instance that is being adapted (the adaptee).

Finally the adapter translates the methods that belong to the interface that it is implementing into the adaptee methods.

We may now pass our existing rectangle wrapped inside the adapter to the client method, as we will see in the last section.

Testing

As a final step we define a simple testing class:

Main.java

package com.byteslounge;

import com.byteslounge.geometry.SomeRectangle;
import com.byteslounge.shape.Rectangle;
import com.byteslounge.shape.adapter.RectangleAdapter;
import com.byteslounge.util.ShapeUtils;

public class Main {

  public static void main(String[] args) {
    OtherRectangle rectangle = new SomeRectangle(2, 5, 10, 14);
    Rectangle adapter = new RectangleAdapter(rectangle);
    ShapeUtils.drawRectangle(adapter);
  }

}

The class generates the following output:

Drawing rectangle [topLeftX: 2, topLeftY: 5, width: 8, height: 9]

We have adapted an existing rectangle interface and implementation into another rectangle interface that was expected by a client.

Download source code from this article

Related Articles

Comments

About the author
Gonçalo Marques is a Software Engineer with several years of experience in software development and architecture definition. During this period his main focus was delivering software solutions in banking, telecommunications and governmental areas. He created the Bytes Lounge website with one ultimate goal: share his knowledge with the software development community. His main area of expertise is Java and open source.

GitHub profile: https://github.com/gonmarques

He is also the author of the WiFi File Browser Android application: