Adapter Pattern
The Adapter is a structural design pattern that enables incompatible interfaces to collaborate by wrapping an existing class (adaptee) with a new compatible interface (adapter). It acts as a "translator" or "wrapper," allowing legacy code or third-party libraries to integrate without modification.
Key Principles
- Interface Compatibility: Convert one interface to match another.
- Composition over Inheritance: Adapter "has-a" adaptee (delegation).
- Single Responsibility: Adapter focuses only on translation.
Structure
- Target: Desired interface (what client expects).
- Adaptee: Existing incompatible class.
- Adapter: Implements Target, delegates to Adaptee.
- Client: Uses Target without knowing about Adapter/Adaptee.
Python Example: Logger Adapter
from abc import ABC, abstractmethod
# Target Interface (what client expects)
class Logger(ABC):
@abstractmethod
def log(self, message: str):
pass
# Adaptee (incompatible third-party logger)
class ThirdPartyLogger:
def write_message(self, msg: str):
print(f"Third-party log: {msg.upper()}") # Different method name
# Adapter (wraps Adaptee to match Target)
class ThirdPartyLoggerAdapter(Logger):
def __init__(self, third_party_logger: ThirdPartyLogger):
self.logger = third_party_logger # Composition
def log(self, message: str):
# Translate: call adaptee's method
self.logger.write_message(message)
# Client Code
if __name__ == "__main__":
# Client expects Logger interface
client_logger = ThirdPartyLoggerAdapter(ThirdPartyLogger())
client_logger.log("Hello, Adapter!") # Outputs: Third-party log: HELLO, ADAPTER!
Output:
Third-party log: HELLO, ADAPTER!
When to Use Adapter
- Legacy Integration: Adapt old code to new interfaces.
- Third-Party Libraries: Wrap incompatible APIs.
- Avoid: When interfaces are similar (use bridge for abstraction).
Summary: Adapter "translates" incompatible interfaces via wrapping, enabling seamless collaboration without altering existing code.