Adapter vs Decorator Pattern: Key Differences and Usage
Adapter pattern changes an object's interface to match what a client expects, enabling incompatible interfaces to work together. The Decorator pattern adds new behavior to an object dynamically without changing its interface, enhancing functionality transparently.Quick Comparison
This table summarizes the main differences between the Adapter and Decorator patterns.
| Aspect | Adapter Pattern | Decorator Pattern |
|---|---|---|
| Purpose | Convert interface of one class to another expected by client | Add new behavior or responsibilities to an object dynamically |
| Interface Change | Yes, adapts to a different interface | No, keeps the original interface intact |
| Focus | Compatibility between different interfaces | Enhancing or extending functionality |
| Usage Scenario | When existing class interface is incompatible | When you want to add features without subclassing |
| Object Wrapping | Wraps an object to change its interface | Wraps an object to add behavior |
| Effect on Client | Client uses adapted interface | Client uses same interface with added features |
Key Differences
The Adapter pattern acts like a translator between two incompatible interfaces. It wraps an existing object and exposes a new interface that the client expects. This pattern is useful when you want to reuse a class but its interface does not match what your code needs.
In contrast, the Decorator pattern wraps an object to add new behaviors or responsibilities without changing its interface. It allows you to extend functionality dynamically and transparently, meaning the client still interacts with the object as usual but with enhanced features.
While both patterns use wrapping, Adapter changes the interface to achieve compatibility, whereas Decorator preserves the interface and focuses on adding capabilities.
Code Comparison
interface Target { void request(); } // Existing incompatible class class Adaptee { void specificRequest() { System.out.println("Adaptee specific request"); } } // Adapter makes Adaptee compatible with Target class Adapter implements Target { private Adaptee adaptee; Adapter(Adaptee adaptee) { this.adaptee = adaptee; } public void request() { adaptee.specificRequest(); } } public class Main { public static void main(String[] args) { Adaptee adaptee = new Adaptee(); Target adapter = new Adapter(adaptee); adapter.request(); } }
Decorator Equivalent
interface Component { void operation(); } // Concrete component class ConcreteComponent implements Component { public void operation() { System.out.println("ConcreteComponent operation"); } } // Decorator adds behavior class Decorator implements Component { protected Component component; Decorator(Component component) { this.component = component; } public void operation() { component.operation(); addedBehavior(); } void addedBehavior() { System.out.println("Decorator added behavior"); } } public class Main { public static void main(String[] args) { Component component = new ConcreteComponent(); Component decorated = new Decorator(component); decorated.operation(); } }
When to Use Which
Choose Adapter when you have an existing class with an interface that does not match what your client code expects, and you want to make it compatible without changing the original class.
Choose Decorator when you want to add new features or responsibilities to objects dynamically and transparently, without altering their interface or using subclassing.
In short, use Adapter for interface compatibility and Decorator for flexible behavior extension.