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.