Use when you need to use an existing class but its interface is incompatible with what your code expects — converting one interface into another without modifying either.
Scanned 9/8/2026
Install to Claude Code
npx -y skills add jeffreytse/grimoire-core --skill apply-adapter-pattern --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Apply Adapter Pattern?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/jeffreytse-apply-adapter-pattern)More formats (shields.io, HTML) on the badges page.
---
name: apply-adapter-pattern
description: Use when you need to use an existing class but its interface is incompatible with what your code expects — converting one interface into another without modifying either.
source: "Gamma, Helm, Johnson, Vlissides, \"Design Patterns: Elements of Reusable Object-Oriented Software\" (1994) pp. 139–150; Java InputStreamReader (adapts InputStream to Reader); Python io.TextIOWrapper; Django REST Framework serializers"
tags: [design-patterns, structural, adapter, oop, developer, interface-compatibility, legacy-integration]
related: [apply-facade-pattern, apply-solid-principles]
---
# Apply Adapter Pattern
Convert the interface of a class into the interface clients expect, enabling classes with incompatible interfaces to work together.
## Why This Is Best Practice
**Adopted by:** Java's `InputStreamReader` (adapts byte-stream `InputStream` to
character-stream `Reader` — in every JVM), Python's `io.TextIOWrapper` (same
adaptation in the standard library), Django REST Framework's Serializer (adapts ORM
models to JSON-serializable dicts), and every database driver layer (JDBC, ODBC) which
adapts vendor-specific protocols to a standard interface.
**Impact:** GoF documents that the Adapter is the primary mechanism for integrating
legacy or third-party code into a new architecture without modifying either side.
Every major language's standard library uses it to bridge abstraction layers — evidence
of universal adoption across all major ecosystems.
**Why best:** The alternative — modifying the existing class — creates coupling to the
consumer and may be impossible (third-party code, compiled libraries). The alternative
of modifying the client breaks encapsulation. Adapter wraps without touching either side.
Sources: Gamma et al. (1994) pp. 139–150; Java I/O class hierarchy documentation;
Python `io` module documentation
## Steps
### Step 1: Identify the target interface the client expects
```python
from abc import ABC, abstractmethod
class JSONLogger(ABC):
@abstractmethod
def log(self, event: dict) -> None: ...
```
### Step 2: Identify the adaptee (existing class with incompatible interface)
```python
class LegacyLogger:
def write_line(self, timestamp: str, level: str, message: str) -> None:
print(f"[{timestamp}] {level}: {message}")
```
### Step 3: Write the adapter — wraps adaptee, implements target interface
```python
from datetime import datetime
class LegacyLoggerAdapter(JSONLogger):
def __init__(self, legacy: LegacyLogger):
self._legacy = legacy
def log(self, event: dict) -> None:
self._legacy.write_line(
timestamp=event.get("ts", datetime.now().isoformat()),
level=event.get("level", "INFO"),
message=event.get("message", ""),
)
```
### Step 4: Wire the adapter at the composition root
```python
legacy = LegacyLogger()
logger: JSONLogger = LegacyLoggerAdapter(legacy)
# Client uses only the target interface — unaware of LegacyLogger
logger.log({"level": "ERROR", "message": "payment failed", "ts": "2026-06-10T12:00:00"})
```
### Step 5: For two-way adaptation, create bidirectional adapters
If both sides need to call each other, write two adapters — one for each direction.
A single class implementing both interfaces creates hidden coupling and is hard to test.
## When NOT to Use
- **When you control both sides** — if you own both the client and the class being used, change one interface to match the other directly. Adapter is for cases where modification is impossible or undesirable.
- **When the interfaces differ too fundamentally** — if the adaptee's semantics are incompatible (not just syntactically different), an adapter produces a leaky abstraction. Consider a `apply-facade-pattern` instead.
## Common Mistakes
**Adapting too much.** An adapter that translates complex business logic is a Facade or a service, not an adapter. Keep adapters thin — only interface translation, no logic.
**Modifying the adaptee to make the adapter simpler.** If you can modify the adaptee, there may be no need for an adapter at all. If you can't, don't.
**Forgetting to handle adapter errors.** When the adaptee throws an exception using its own error types, the adapter should either translate them to the target interface's error types or let them propagate with clear context.Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!