Overview
Definition
Adapter: structural design pattern. Purpose: convert interface of a class into another interface clients expect. Effect: enables collaboration between incompatible interfaces without modifying their source code.
Context
Problem: integrating legacy or third-party components with incompatible interfaces. Solution: introduce an adapter layer to translate calls.
Scope
Scope: applies to object-oriented systems requiring interface compatibility. Common in APIs, frameworks, middleware, and refactoring legacy code.
"The adapter pattern allows classes to work together that otherwise couldn't because of incompatible interfaces." -- Erich Gamma et al.
Intent
Primary Goal
Allow incompatible interfaces to collaborate by wrapping one interface with another.
Key Benefits
Promotes reusability, flexibility, and code decoupling. Avoids rewriting existing code. Supports separation of concerns.
Typical Use Cases
Legacy system integration, third-party API adaptation, interface standardization, protocol translation.
Motivation
Problem Scenario
Legacy audio player supports only MP3. New media library supports VLC formats. Direct integration impossible due to incompatible interfaces.
Adapter Solution
Introduce adapter class implementing expected interface. Adapter translates calls to legacy player methods, enabling new media to play.
Outcome
Seamless integration without modifying existing player code. Client code remains unchanged.
Structure
Class Diagram
Participants: Target, Client, Adaptee, Adapter. Adapter implements Target interface and holds reference to Adaptee.
Component Descriptions
Client: interacts with Target interface. Target: defines domain-specific interface. Adaptee: existing incompatible interface. Adapter: bridges Target and Adaptee.
Diagram Illustration
+-----------+ +-----------+| Client | | Adaptee || | | || uses | | specific || Target | | interface |+-----+-----+ +-----+-----+ | ^ | implements Target |+-----v-----+ || Adapter |-------------------+| | holds reference+-----------+ Participants
Client
Class that requires services defined by Target interface.
Target
Defines interface expected by Client.
Adaptee
Existing class with incompatible interface.
Adapter
Implements Target interface. Converts Client calls to Adaptee calls.
Collaborations
Call Flow
Client calls Adapter methods via Target interface. Adapter translates calls to Adaptee methods.
Data Transformation
Adapter may convert data formats, parameters, or semantics between Target and Adaptee.
Object Management
Adapter holds instance or reference of Adaptee to forward requests.
Consequences
Advantages
Enables reuse of existing components. Promotes flexibility and extensibility. Localizes interface translation.
Disadvantages
Additional complexity and indirection. Potential performance overhead. Adapter proliferation if overused.
Trade-offs
Balance between adaptation benefits and added maintenance cost.
Implementation
Object Adapter
Adapter holds instance of Adaptee. Implements Target interface. Uses composition.
Class Adapter
Adapter inherits Adaptee and implements Target. Uses multiple inheritance (supported in some languages).
Code Example (Object Adapter)
class Target { virtual void request() = 0;};class Adaptee {public: void specificRequest();};class Adapter : public Target {private: Adaptee* adaptee;public: Adapter(Adaptee* a) : adaptee(a) {} void request() override { adaptee->specificRequest(); }}; Variants
Class Adapter Pattern
Uses multiple inheritance to adapt interface.
Object Adapter Pattern
Uses composition to wrap Adaptee object.
Two-Way Adapter
Adapter supports both Target and Adaptee interfaces for bidirectional compatibility.
Applicability
When to Use
Existing class interfaces incompatible but must collaborate. Reuse required without source modification. Interface standardization needed.
When to Avoid
If interface incompatibility can be resolved by redesigning interfaces. Avoid excessive adapters causing complexity.
Best Practices
Keep adapter simple and focused. Document interface mappings clearly. Test adapters thoroughly.
Examples
Real-World Example
Power plug adapter: converts plug shapes and voltages for compatibility.
Software Example
Java I/O streams: InputStreamReader adapts byte streams to character streams.
Code Snippet
// Adaptee interfaceclass LegacyRectangle {public: void draw(int x1, int y1, int x2, int y2);};// Target interfaceclass Shape {public: virtual void draw(int x, int y, int width, int height) = 0;};// Adapterclass RectangleAdapter : public Shape {private: LegacyRectangle legacyRectangle;public: void draw(int x, int y, int width, int height) override { legacyRectangle.draw(x, y, x + width, y + height); }}; References
- Gamma, E., Helm, R., Johnson, R., Vlissides, J. Design Patterns: Elements of Reusable Object-Oriented Software. Addison-Wesley, 1994, pp. 139-152.
- Buschmann, F., Meunier, R., Rohnert, H., Sommerlad, P., Stal, M. Pattern-Oriented Software Architecture: A System of Patterns. Wiley, 1996, pp. 165-181.
- Freeman, E., Robson, E., Bates, B., Sierra, K. Head First Design Patterns. O'Reilly Media, 2004, pp. 193-217.
- Shalloway, A., Trott, J.R. Design Patterns Explained: A New Perspective on Object-Oriented Design. Addison-Wesley, 2004, pp. 123-140.
- Meszaros, G. Object-Oriented Design Patterns in C++. Addison-Wesley, 1997, pp. 89-104.