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:
- Reuse existing classes with incompatible interfaces
- Separates interface conversion from business logic (Single Responsibility)
- Can adapt multiple incompatible classes
- Introduce new adapters without changing existing code (Open/Closed Principle)
Drawbacks:
- Adds extra classes and indirection increasing complexity
- Additional layer adds performance overhead
- Can make code harder to understand
- 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
Related Patterns¶
- 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
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
write_log(message)
¶
Writes a log message.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
message
|
str
|
The log message to write. |
required |
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
__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 |
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 |
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 |
LoggerInterface
¶
The target interface that clients will use.
This interface defines the logging methods that the application expects.