Skip to content

Adapter Pattern

Category: Structural Pattern

Overview

Convert the interface of a class into another interface clients expect. This pattern allows classes to work together that couldn't otherwise because of incompatible interfaces, acting as a bridge between two incompatible interfaces.

Usage Guidelines

Use when:

  • Want to use class with incompatible interface
  • Need to integrate legacy code with new systems
  • Library interface doesn't match your needs
  • Want to provide uniform interface to related classes

Avoid when:

  • Interfaces are already compatible
  • Can modify the original class interface
  • A simple wrapper function suffices
  • Pattern adds unnecessary complexity

Implementation

class LoggerInterface:
    """The target interface that clients will use.

    This interface defines the logging methods that the application expects.
    """
    def log_info(self, message: str) -> None:
        """Logs an informational message.

        Args:
            message: The message to log as informational.
        """
        raise NotImplementedError

    def log_error(self, message: str) -> None:
        """Logs an error message.

        Args:
            message: The message to log as an error.
        """
        raise NotImplementedError

class LegacyLogger:
    """A legacy logging system.

    This class represents an existing logging system that has its own interface.
    It logs messages to the console.
    """
    def write_log(self, message: str) -> None:
        """Writes a log message.

        Args:
            message: The log message to write.
        """
        print(f"Legacy Log: {message}")

class LoggerAdapter(LoggerInterface):
    """An adapter for the legacy logger.

    This adapter adapts the LegacyLogger to the LoggerInterface, allowing
    the application to use the legacy logging system through the new interface.
    """

    def __init__(self, legacy_logger: LegacyLogger):
        """Initializes the LoggerAdapter with a legacy logger.

        Args:
            legacy_logger: An instance of the legacy logger.
        """
        self._legacy_logger = legacy_logger

    def log_info(self, message: str) -> None:
        """Logs an informational message using the legacy logger.

        Args:
            message: The message to log as informational.
        """
        self._legacy_logger.write_log(f"INFO: {message}")

    def log_error(self, message: str) -> None:
        """Logs an error message using the legacy logger.

        Args:
            message: The message to log as an error.
        """
        self._legacy_logger.write_log(f"ERROR: {message}")

Usage

# Create legacy logger
legacy_logger = LegacyLogger()

# Adapt it to new interface
logger = LoggerAdapter(legacy_logger)

# Use with new interface
logger.log_info("This is an informational message.")
# Output: Legacy Log: INFO: This is an informational message.

logger.log_error("This is an error message.")
# Output: Legacy Log: ERROR: This is an error message.

Trade-offs

Benefits:

  1. Reuse existing classes with incompatible interfaces
  2. Separates interface conversion from business logic (Single Responsibility)
  3. Can adapt multiple incompatible classes
  4. Introduce new adapters without changing existing code (Open/Closed Principle)

Drawbacks:

  1. Adds extra classes and indirection increasing complexity
  2. Additional layer adds performance overhead
  3. Can make code harder to understand
  4. Many adapters can clutter codebase

Real-World Examples

  • Database adapters for different database drivers
  • Payment gateways adapting different providers
  • File format converters
  • API wrappers for REST APIs
  • Bridge
  • Decorator
  • Facade
  • Proxy

API Reference

design_patterns.structural.adapter

Adapter Pattern Example

This module demonstrates the Adapter Pattern, which allows incompatible interfaces to work together. In this example, a legacy logging system is adapted to conform to a new logging interface.

Classes:

Name Description
LoggerInterface

The target interface that clients will use.

LegacyLogger

A legacy logging system that writes logs to the console.

LoggerAdapter

An adapter that allows the legacy logging system to be used with the new interface.

Usage

Create an instance of LegacyLogger and wrap it with LoggerAdapter. Then use the adapter to log messages using the log_info and log_error methods.

Example
legacy_logger = LegacyLogger()
logger = LoggerAdapter(legacy_logger)
logger.log_info("This is an informational message.")
> Legacy Log: INFO: This is an informational message.

logger.log_error("This is an error message.")
> Legacy Log: ERROR: This is an error message.

LegacyLogger

A legacy logging system.

This class represents an existing logging system that has its own interface. It logs messages to the console.

Source code in src/design_patterns/structural/adapter.py
class LegacyLogger:
    """A legacy logging system.

    This class represents an existing logging system that has its own interface.
    It logs messages to the console.
    """
    def write_log(self, message: str) -> None:
        """Writes a log message.

        Args:
            message (str): The log message to write.
        """
        print(f"Legacy Log: {message}")

write_log(message)

Writes a log message.

Parameters:

Name Type Description Default
message str

The log message to write.

required
Source code in src/design_patterns/structural/adapter.py
def write_log(self, message: str) -> None:
    """Writes a log message.

    Args:
        message (str): The log message to write.
    """
    print(f"Legacy Log: {message}")

LoggerAdapter

Bases: LoggerInterface

An adapter for the legacy logger.

This adapter adapts the LegacyLogger to the LoggerInterface, allowing the application to use the legacy logging system through the new interface.

Source code in src/design_patterns/structural/adapter.py
class LoggerAdapter(LoggerInterface):
    """An adapter for the legacy logger.

    This adapter adapts the LegacyLogger to the LoggerInterface, allowing
    the application to use the legacy logging system through the new interface.
    """

    def __init__(self, legacy_logger: LegacyLogger):
        """Initializes the LoggerAdapter with a legacy logger.

        Args:
            legacy_logger (LegacyLogger): An instance of the legacy logger.
        """
        self._legacy_logger = legacy_logger

    def log_info(self, message: str) -> None:
        """Logs an informational message using the legacy logger.

        Args:
            message (str): The message to log as informational.
        """
        self._legacy_logger.write_log(f"INFO: {message}")

    def log_error(self, message: str) -> None:
        """Logs an error message using the legacy logger.

        Args:
            message (str): The message to log as an error.
        """
        self._legacy_logger.write_log(f"ERROR: {message}")

__init__(legacy_logger)

Initializes the LoggerAdapter with a legacy logger.

Parameters:

Name Type Description Default
legacy_logger LegacyLogger

An instance of the legacy logger.

required
Source code in src/design_patterns/structural/adapter.py
def __init__(self, legacy_logger: LegacyLogger):
    """Initializes the LoggerAdapter with a legacy logger.

    Args:
        legacy_logger (LegacyLogger): An instance of the legacy logger.
    """
    self._legacy_logger = legacy_logger

log_error(message)

Logs an error message using the legacy logger.

Parameters:

Name Type Description Default
message str

The message to log as an error.

required
Source code in src/design_patterns/structural/adapter.py
def log_error(self, message: str) -> None:
    """Logs an error message using the legacy logger.

    Args:
        message (str): The message to log as an error.
    """
    self._legacy_logger.write_log(f"ERROR: {message}")

log_info(message)

Logs an informational message using the legacy logger.

Parameters:

Name Type Description Default
message str

The message to log as informational.

required
Source code in src/design_patterns/structural/adapter.py
def log_info(self, message: str) -> None:
    """Logs an informational message using the legacy logger.

    Args:
        message (str): The message to log as informational.
    """
    self._legacy_logger.write_log(f"INFO: {message}")

LoggerInterface

The target interface that clients will use.

This interface defines the logging methods that the application expects.

Source code in src/design_patterns/structural/adapter.py
class LoggerInterface:
    """The target interface that clients will use.

    This interface defines the logging methods that the application expects.
    """
    def log_info(self, message: str) -> None:
        """Logs an informational message.

        Args:
            message (str): The message to log as informational.
        """
        raise NotImplementedError

    def log_error(self, message: str) -> None:
        """Logs an error message.

        Args:
            message (str): The message to log as an error.
        """
        raise NotImplementedError

log_error(message)

Logs an error message.

Parameters:

Name Type Description Default
message str

The message to log as an error.

required
Source code in src/design_patterns/structural/adapter.py
def log_error(self, message: str) -> None:
    """Logs an error message.

    Args:
        message (str): The message to log as an error.
    """
    raise NotImplementedError

log_info(message)

Logs an informational message.

Parameters:

Name Type Description Default
message str

The message to log as informational.

required
Source code in src/design_patterns/structural/adapter.py
def log_info(self, message: str) -> None:
    """Logs an informational message.

    Args:
        message (str): The message to log as informational.
    """
    raise NotImplementedError