What It Is
The Adapter pattern makes a class with the wrong interface fit the interface your code expects — like a travel plug between a foreign socket and your charger.
You cannot change the old class (it comes from a library, or other code depends on it). So you wrap it in a small class that implements the interface you need and translates the calls.
Example: An Old Printer as a Logger
Our code wants a Logger with log($message). The legacy class only offers writeLine($prefix, $text). The adapter holds the old class and translates one call into the other.
The adapter translates the call
run() works with any Logger; the adapter makes the legacy printer one of them without touching its code.
Output: PHP script
<?php
// The interface our code expects.
interface Logger {
public function log(string $message): void;
}
// An old library class with a different method — we cannot change it.
class LegacyPrinter {
public function writeLine(string $prefix, string $text): void {
echo $prefix . " | " . $text, "\n";
}
}
// The adapter makes the old class fit the new interface.
class PrinterAdapter implements Logger {
public function __construct(private LegacyPrinter $legacy) {}
public function log(string $message): void {
$this->legacy->writeLine("LOG", $message);
}
}
function run(Logger $l): void {
$l->log("service started");
$l->log("request handled");
}
run(new PrinterAdapter(new LegacyPrinter()));
When to Use It
Use it when working code and a needed interface do not match, and you cannot (or should not) change either side — third-party libraries, legacy code, generated code.
Skip it when you own both sides — then just change one of them to match. An adapter between two classes you control is a workaround for a decision you could simply make.
Pros and Cons
Pros: old and new code cooperate without changes to either; the translation lives in one small, testable class.
Cons: one more hop to read through; many adapters stacked on each other are a sign the interfaces themselves need rework.