Adapter Pattern¶
Intent¶
The Adapter pattern converts the interface of a class into another interface clients expect. Adapter lets classes work together that couldn't otherwise because of incompatible interfaces.
Also known as: Wrapper
Problem¶
You want to use an existing class, but its interface doesn't match the one you need. This commonly occurs when:
- You need to integrate a third-party library with an incompatible interface
- You want to reuse legacy code in a new system
- You need to create a reusable class that cooperates with unrelated or unforeseen classes
Solution¶
The Adapter pattern suggests creating a wrapper class (the Adapter) that translates the interface of one class (the Adaptee) into an interface expected by the client (the Target). The adapter implements the Target interface and translates calls to the Adaptee's interface.
Structure¶
Object Adapter (using composition)¶
Class Adapter (using multiple inheritance)¶
Participants¶
- Target
- Defines the domain-specific interface that Client uses
- In our example:
Targetclass withrequest()method
- Client
- Collaborates with objects conforming to the Target interface
- In our example:
clientCode()function
- Adaptee
- Defines an existing interface that needs adapting
- In our example:
Adapteeclass withspecificRequest()method
- Adapter
- Adapts the interface of Adaptee to the Target interface
- In our example:
Adapter(object adapter) andClassAdapter(class adapter)
Implementation¶
Object Adapter¶
Class Adapter¶
Usage Example¶
Applicability¶
Use the Adapter pattern when:
- You want to use an existing class with an incompatible interface - The adapter provides the interface clients expect while using the existing class
- You want to create a reusable class that cooperates with unrelated classes - The adapter can work with classes that don't necessarily have compatible interfaces
- You need to use several existing subclasses but can't adapt their interface by subclassing - Object adapter can adapt the interface of its parent class
Consequences¶
Benefits¶
-
Single Responsibility Principle - You can separate the interface or data conversion code from the primary business logic
-
Open/Closed Principle - You can introduce new types of adapters without breaking existing client code
-
Flexibility - Object adapter can adapt many adaptees (the adaptee and all its subclasses)
-
Reusability - Can reuse existing functionality without modifying the source code
Drawbacks¶
-
Increased complexity - Overall complexity increases because you need to introduce new interfaces and classes
-
Performance overhead - Additional layer of indirection (though usually negligible)
Object Adapter vs Class Adapter¶
Object Adapter (Composition)¶
Advantages: - Can adapt the adaptee and all its subclasses - More flexible (can switch adaptees at runtime) - Follows composition over inheritance principle
Disadvantages: - Cannot override Adaptee's behavior - Slightly more complex implementation
Class Adapter (Multiple Inheritance)¶
Advantages: - Can override Adaptee's behavior - No extra pointer indirection - Slightly more efficient
Disadvantages: - Only adapts the specific Adaptee class - Not available in languages without multiple inheritance (Java, C#) - Tighter coupling between Adapter and Adaptee
Real-world Examples¶
-
Legacy System Integration - Adapting old APIs to work with modern code - Example: Wrapping a legacy payment system to work with a new e-commerce platform
-
Third-party Library Integration - Making external libraries compatible with your interface - Example: Adapting different logging libraries to a common logging interface
-
Data Format Conversion - Converting data between different formats - Example: XML to JSON adapter, or metric to imperial unit adapter
-
GUI Toolkits - Adapting platform-specific UI components to a common interface - Example: Qt, GTK adapters for different operating systems
-
Database Drivers - Providing a uniform interface to different database systems - Example: ODBC/JDBC drivers adapt various databases to standard interfaces
Related Patterns¶
- Bridge: Similar structure but different intent. Bridge separates interface from implementation to allow both to vary independently, while Adapter makes existing interfaces work together.
- Decorator: Changes an object's responsibilities without changing its interface, while Adapter changes the interface.
- Proxy: Provides the same interface as the subject, while Adapter provides a different interface.
- Facade: Defines a new interface for a set of objects, while Adapter reuses an old interface.
Implementation Considerations¶
-
Choose between Object and Class Adapter - Object adapter is more flexible and widely applicable - Class adapter is useful when you need to override adaptee behavior
-
Two-way adapters - Can be bidirectional, allowing the adapted object to be used through both interfaces - Useful for gradual migration from old to new interfaces
-
Pluggable adapters - Design adapters to be easily replaceable - Use interfaces and dependency injection
-
Minimizing adaptation - Keep the amount of work the adapter has to do minimal - Consider if the adaptee interface could be changed instead
Sample Output¶
Key Takeaways¶
- Adapter makes incompatible interfaces work together
- Two implementation approaches: object adapter (composition) and class adapter (inheritance)
- Object adapter is more flexible and commonly preferred
- Essential for integrating legacy code and third-party libraries
- Follows the Open/Closed Principle - extends functionality without modification