From Heterogeneous to Homogeneous: How Adapter Design Pattern Can Help

Learn how the adapter design pattern can help you bridge the gap between incompatible interfaces and make your code more flexible and adaptable.

Avinash Tingre
5 min readFeb 6, 2023
Photo by Samsung Memory on Unsplash

Have you ever tried to fit a square peg into a round hole? It’s a frustrating experience, right? But what if there was a tool that could help you make it work?

Well, that’s exactly what the adapter design pattern does. In software development, it helps classes work together that otherwise wouldn’t, just like that tool helps the square peg fit into the round hole.

What is the Adapter Design Pattern?

The adapter design pattern is a structural design pattern that allows two incompatible classes to work together. It does this by converting the interface of one class into an interface that another class expects.

Think of it as a language interpreter. If you speak English and someone speaks French, you can’t communicate unless you have an interpreter. The adapter design pattern acts as that interpreter, allowing the two classes to communicate and work together, even though they don’t speak the same language.

Why Use the Adapter Design Pattern?

There are many reasons to use the adapter design pattern, but here are a few key benefits:

  • Reuse of existing classes: You can reuse existing classes without having to modify them, making your code more efficient and easier to maintain.
  • Flexibility: The adapter design pattern allows you to easily change the class you’re using without affecting the rest of your code, giving you greater flexibility in your development.
  • Ease of use: By using the adapter design pattern, you can make complex systems easier to use and understand, making your code more user-friendly.

How to Use the Adapter Design Pattern

The adapter design pattern is easy to use and implement. Here’s a step-by-step guide:

  1. Identify the class that you want to use.
  2. Create a new class that implements the required interface.
  3. In the new class, add a reference to the class you want to use.
  4. In the new class, write the code to translate the requests and responses between the two classes.

Let’s imagine you’re developing a gaming platform that supports different types of games such as racing, shooting, and puzzle games. Each game has its own unique controls, making it difficult to provide a consistent user experience across all games. This is where the adapter design pattern comes in.

How to Implement the Adapter Design Pattern in Java

Here’s how you can implement the adapter design pattern in your gaming platform:

  1. Create an interface called GameControls with methods such as moveUp, moveDown, moveLeft, and moveRight. This interface will be used to provide a consistent set of controls for all games.
public interface GameControls {
void moveUp();
void moveDown();
void moveLeft();
void moveRight();
}

2. Create a class called RacingGameControls that implements the GameControls interface. This class will be used to provide controls for racing games.

public class RacingGameControls implements GameControls {
@Override
public void moveUp() {
System.out.println("Accelerating");
}

@Override
public void moveDown() {
System.out.println("Braking");
}

@Override
public void moveLeft() {
System.out.println("Turning left");
}

@Override
public void moveRight() {
System.out.println("Turning right");
}
}

3. Create a class called ShootingGameControls that implements the GameControls interface. This class will be used to provide controls for shooting games.

public class ShootingGameControls implements GameControls {
@Override
public void moveUp() {
System.out.println("Aiming up");
}

@Override
public void moveDown() {
System.out.println("Aiming down");
}

@Override
public void moveLeft() {
System.out.println("Aiming left");
}

@Override
public void moveRight() {
System.out.println("Aiming right");
}
}

4. Create a class called PuzzleGameControls that provides controls for puzzle games. This class doesn't implement the GameControls interface, so you'll need to create an adapter class to adapt its controls to the GameControls interface.

public class PuzzleGameControls {
public void moveUp() {
System.out.println("Moving piece up");
}

public void moveDown() {
System.out.println("Moving piece down");
}

public void moveLeft() {
System.out.println("Moving piece left");
}

public void moveRight() {
System.out.println("Moving piece right");
}
}

5. Create an adapter class called PuzzleGameControlsAdapter that implements the GameControls interface and adapts the PuzzleGameControls class to the GameControls interface.

public class PuzzleGameControlsAdapter implements GameControls {
private PuzzleGameControls puzzleControls;

public PuzzleGameControlsAdapter(PuzzleGameControls puzzleControls) {
this.puzzleControls = puzzleControls;
}

@Override
public void moveUp() {
puzzleControls.moveUp();
}

@Override
public void moveDown() {
puzzleControls.moveDown();
}

@Override
public void moveLeft() {
puzzleControls.moveLeft();
}

@Override
public void moveRight() {
puzzleControls.moveRight();
}
}

6. To use the adapter pattern in your gaming platform, you can create a class called GamePlayer that takes a GameControls object and allows the user to play the game.

public class GamePlayer {
private GameControls controls;

public GamePlayer(GameControls controls) {
this.controls = controls;
}

public void playGame() {
controls.moveUp();
controls.moveDown();
controls.moveLeft();
controls.moveRight();
}
}

7. Finally, to play different types of games, you can create instances of the RacingGameControls, ShootingGameControls, and PuzzleGameControlsAdapter classes and pass them to the GamePlayer class.

public class Main {
public static void main(String[] args) {
GameControls racingControls = new RacingGameControls();
GamePlayer racingPlayer = new GamePlayer(racingControls);
racingPlayer.playGame();

GameControls shootingControls = new ShootingGameControls();
GamePlayer shootingPlayer = new GamePlayer(shootingControls);
shootingPlayer.playGame();

PuzzleGameControls puzzleControls = new PuzzleGameControls();
GameControls puzzleAdapter = new PuzzleGameControlsAdapter(puzzleControls);
GamePlayer puzzlePlayer = new GamePlayer(puzzleAdapter);
puzzlePlayer.playGame();
}
}

Examples of the Adapter Design Pattern

Here are a few real-world examples of how the adapter design pattern is used:

  • Plug adapters: A plug adapter is a physical example of the adapter design pattern. It allows you to use a plug from one country in a socket from another country, even though they are incompatible.
  • File format converters: Many software programs allow you to convert files from one format to another, such as converting a .docx file to a .pdf file. This is a classic example of the adapter design pattern in action.
  • Mobile phone chargers: Mobile phone chargers are another example of the adapter design pattern. They allow you to use your phone with different types of charging ports, even if they are not compatible.

In Conclusion

The adapter design pattern is a powerful tool in software development, allowing incompatible classes to work together and making complex systems easier to use and understand. By using the adapter design pattern, you can make your code more efficient, flexible, and user-friendly.

So, next time you’re faced with the challenge of trying to fit a square peg into a round hole, remember the adapter design pattern and how it can help make your development easier and more successful.

Thanks for reading till end, happy learning 🍀

--

--

Avinash Tingre

Software Engineer. Jack of all trades; master of none :)