Java Adapter Pattern example
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:
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:
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:
package com.byteslounge.geometry; public interface OtherRectangle { int getTopLeftX(); int getTopLeftY(); int getBottomRightX(); int getBottomRightY(); }
And the corresponding implementation:
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:
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:
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:
We have adapted an existing rectangle interface and implementation into another rectangle interface that was expected by a client.