d9e5668cec
Fixes and improvements from exhaustive audit: Consistency fixes in SKILL.md: - 'Pipe & Filter' → 'Pipe and Filter' (one stray '&' found and corrected) - 'Singleton for factory instance' → clarified to 'register factory as singleton-scoped via DI container' (less misleading wording) - Documentation Format section updated with note that SKILL.md itself is the authoritative source for related-pattern combinations Coverage fix — Related Patterns sections: - Added '## Related Patterns' to ALL 94 pattern files (was 0/94) - Each section lists 3–6 related patterns with relationship descriptions - Covers: why they're related, when to prefer one vs the other, and which are often confused SOLID principles → Creational → Structural → Behavioral → Architectural → Concurrency → Functional → Resilience → Data Access → Messaging → Testing → Error Handling → Microservice — all 13 categories covered Code verification: - Python: 0 failures (all 85 testable blocks pass) - Go: 0 failures (all 76 testable blocks pass) - JavaScript: 0 failures (all 78 testable blocks pass) - All 239 code blocks verified correct after edits Final skill state: - 108 files, 36,524 lines across 13 reference categories - 94/94 pattern files have Related Patterns sections - 2,815-line SKILL.md with 67 decision trees, 23 scenarios, 0 broken references, 0 naming inconsistencies
5.9 KiB
5.9 KiB
Adapter Pattern
Convert the interface of a class into another interface clients expect. Adapter lets classes work together that couldn't otherwise because of incompatible interfaces.
Problem
You have a LegacyPrinter with a print_old(text) method, but your application expects a Printer interface with a print(text) method. You cannot modify the legacy code.
Solution
Create an Adapter class that implements the target Printer interface and internally delegates to the LegacyPrinter's incompatible method.
┌────────┐ ┌─────────────────┐ ┌───────────────┐
│ Client │──────▶│ Printer (intf) │ │ LegacyPrinter │
│ │ │ + print(text) │ │ + print_old() │
└────────┘ └─────────────────┘ └───────────────┘
▲ ▲
│ │
┌────────┴────────┐ │
│ PrinterAdapter │───────────────┘
│ + print(text) │ delegates to print_old()
└─────────────────┘
When to Use
- You need to use an existing class but its interface doesn't match what you need.
- You want to create a reusable class that cooperates with unrelated or unforeseen classes.
- You need to integrate third-party or legacy code without modifying it.
When to Avoid
- The interfaces are already compatible — just use the object directly.
- You can modify the source of the incompatible class (refactor instead).
- The adaptation is so complex that a full rewrite would be simpler.
Pseudocode
interface Printer:
method print(text)
class LegacyPrinter:
method print_old(text):
output "[Legacy] " + text
class PrinterAdapter implements Printer:
field legacy: LegacyPrinter
constructor(legacy):
this.legacy = legacy
method print(text):
legacy.print_old(text)
// Usage
legacy = new LegacyPrinter()
adapter = new PrinterAdapter(legacy)
adapter.print("Hello, Adapter!")
Python
from abc import ABC, abstractmethod
class Printer(ABC):
"""Target interface the client expects."""
@abstractmethod
def print(self, text: str) -> None: ...
class LegacyPrinter:
"""Existing class with an incompatible interface."""
def print_old(self, text: str) -> None:
print(f"[Legacy] {text}")
class PrinterAdapter(Printer):
"""Adapter that wraps LegacyPrinter to satisfy the Printer interface."""
def __init__(self, legacy: LegacyPrinter) -> None:
self._legacy = legacy
def print(self, text: str) -> None:
self._legacy.print_old(text)
def client_code(printer: Printer) -> None:
"""Client only knows about the Printer interface."""
printer.print("Hello from the client!")
if __name__ == "__main__":
legacy = LegacyPrinter()
adapter = PrinterAdapter(legacy)
print("--- Direct legacy call ---")
legacy.print_old("direct call")
print("\n--- Through adapter ---")
client_code(adapter)
Output:
--- Direct legacy call ---
[Legacy] direct call
--- Through adapter ---
[Legacy] Hello from the client!
Go
package main
import "fmt"
// Target interface the client expects.
type Printer interface {
Print(text string)
}
// LegacyPrinter has an incompatible method name.
type LegacyPrinter struct{}
func (lp *LegacyPrinter) PrintOld(text string) {
fmt.Printf("[Legacy] %s\n", text)
}
// PrinterAdapter adapts LegacyPrinter to the Printer interface.
type PrinterAdapter struct {
legacy *LegacyPrinter
}
func NewPrinterAdapter(legacy *LegacyPrinter) *PrinterAdapter {
return &PrinterAdapter{legacy: legacy}
}
func (a *PrinterAdapter) Print(text string) {
a.legacy.PrintOld(text)
}
// clientCode only depends on the Printer interface.
func clientCode(p Printer) {
p.Print("Hello from the client!")
}
func main() {
legacy := &LegacyPrinter{}
adapter := NewPrinterAdapter(legacy)
fmt.Println("--- Direct legacy call ---")
legacy.PrintOld("direct call")
fmt.Println("\n--- Through adapter ---")
clientCode(adapter)
}
Output:
--- Direct legacy call ---
[Legacy] direct call
--- Through adapter ---
[Legacy] Hello from the client!
JavaScript
// Target interface expectation: object with print(text)
class LegacyPrinter {
printOld(text) {
console.log(`[Legacy] ${text}`);
}
}
class PrinterAdapter {
constructor(legacyPrinter) {
this._legacy = legacyPrinter;
}
print(text) {
this._legacy.printOld(text);
}
}
function clientCode(printer) {
printer.print("Hello from the client!");
}
// --- Main ---
const legacy = new LegacyPrinter();
const adapter = new PrinterAdapter(legacy);
console.log("--- Direct legacy call ---");
legacy.printOld("direct call");
console.log("\n--- Through adapter ---");
clientCode(adapter);
Output:
--- Direct legacy call ---
[Legacy] direct call
--- Through adapter ---
[Legacy] Hello from the client!
Related Patterns
- Facade — Adapter translates one interface; Facade simplifies a complex subsystem. An Adapter for a complex system can look like a Facade.
- Decorator — Decorator adds behavior while preserving interface; Adapter changes the interface without adding behavior.
- Proxy — Proxy preserves the interface and controls access; Adapter changes the interface.
- Bridge — Adapter reconciles existing incompatible interfaces; Bridge separates abstraction from implementation from the start.
- Strangler Fig — Adapter is the key tool in Strangler Fig migration: the new interface is an Adapter over the old one.