Things you should know about : Class and Object Adapters

Sourabh Kumar
3 min readJul 23, 2024

--

The Adapter Pattern is a fundamental design pattern in object-oriented programming that allows incompatible interfaces to collaborate. Imagine you’re building a game and want to support controllers from various manufacturers, each with a unique button layout. The Adapter Pattern lets you create a bridge so your game code can interact with all controllers seamlessly.

There are two main approaches to implementing the Adapter Pattern: Class Adapters and Object Adapters. Today, we’ll delve into both approaches and explore their strengths and weaknesses, using relatable examples to solidify our understanding.

Object Adapter:

  • In this scenario, the adapter is a separate object that holds a reference to the takeout container (adaptee).
  • Imagine your resourceful chef creates a custom metal holder specifically designed for takeout containers. This holder sits on the French oven rack and securely cradles the takeout container.

Example:

// TakeoutContainer (Adaptee) - Interface for takeout containers
public interface TakeoutContainer {
public void putFood(String food);
}

// Realisation of TakeoutContainer
public class RectangularContainer implements TakeoutContainer {
@Override
public void putFood(String food) {
System.out.println("Putting " + food + " in rectangular takeout container.");
}
}

// FrenchOven (Target Interface) - Interface for the French oven
public interface FrenchOven {
public void placeOnRack(RoundDish dish);
public void cook();
}

// MetalPlateAdapter (Adapter - Object Adapter) - Adapts TakeoutContainer to FrenchOven
public class MetalPlateAdapter implements FrenchOven {
private TakeoutContainer container;
public MetalPlateAdapter(TakeoutContainer container) {
this.container = container;
}
@Override
public void placeOnRack(RoundDish dish) {
// Not applicable for this adapter, handle appropriately (e.g., throw exception)
throw new UnsupportedOperationException("Metal plate doesn't hold round dishes.");
}
@Override
public void cook() {
container.putFood("Food"); // Use the container to put food
System.out.println("Using metal plate adapter to cook food in French oven.");
}
}

// Usage
public class Restaurant {
public static void main(String[] args) {
TakeoutContainer container = new RectangularContainer();
FrenchOven oven = new MetalPlateAdapter(container);
oven.cook();
}
}

In the object adapter example, the MetalPlateAdapter is a separate class that holds a reference to the TakeoutContainer and provides the FrenchOven interface methods.

Which to Use?

  • This is more flexible and commonly used.
  • It can adapt any takeout container to the French oven since it doesn’t modify the takeout container itself.
  • You can also have multiple object adapters for different types of takeout containers.

Class Adapter:

  • Here, the adapter inherits from both the takeout container (adaptee) and the French oven interface (target interface).
  • This approach is like having a specially designed takeout container with a built-in metal base that perfectly fits the French oven rack. It essentially combines the functionalities of both the takeout container and the adapter plate into a single unit.

Example:

// TakeoutContainerWithBase (Adaptee & Adapter - Class Adapter) - Inherits from both TakeoutContainer and FrenchOven
public class TakeoutContainerWithBase extends RectangularContainer implements FrenchOven {
public TakeoutContainerWithBase() {
super(); // Call parent constructor for TakeoutContainer functionality
}
@Override
public void placeOnRack(RoundDish dish) {
// Not applicable for this container, handle appropriately (e.g., throw exception)
throw new UnsupportedOperationException("TakeoutContainerWithBase doesn't hold round dishes.");
}
@Override
public void cook() {
putFood("Food"); // Use the container to put food
System.out.println("Using TakeoutContainerWithBase to cook food in French oven.");
}
}

// Usage (similar to Object Adapter)
public class Restaurant {
public static void main(String[] args) {
FrenchOven oven = new TakeoutContainerWithBase();
oven.cook();
}
}

In the class adapter example, the TakeoutContainerWithBase class inherits from both TakeoutContainer and FrenchOven, directly combining their functionalities.

Which to Use?

  • This is ideal when you have control over the takeout container class and when you only have a limited number of container variations.
  • It can provide a more efficient solution by combining functionalities but might limit future flexibility if new container types arise.

In Conclusion

The Adapter Pattern, with its Class and Object Adapter variations, is a valuable tool for promoting loose coupling and facilitating collaboration between incompatible interfaces in object-oriented programming. By understanding its strengths and weaknesses, you can make informed decisions to enhance the reusability, maintainability, and flexibility of your code.

Do you have any questions or specific scenarios where you think the Adapter Pattern could be useful? Feel free to leave a comment below!

--

--

Sourabh Kumar
Sourabh Kumar

Written by Sourabh Kumar

Software Developer. Tech Enthusiast. Innovative Solver.

No responses yet