Adapter is the right structural move when two useful parts do not fit each other’s interface cleanly.
Convert the interface of one class into another interface clients expect so existing code can work with incompatible collaborators.
What the pattern is trying to do
Convert the interface of one class into another interface clients expect so existing code can work with incompatible collaborators.
What force creates the need
A useful external or legacy component does the right job but exposes the wrong shape for the rest of the system.
How the pattern responds
Wrap the mismatched collaborator in an adapter that translates calls and data into the interface your code expects.
How the moving parts fit together
A client talks to a target interface while the adapter delegates to an adaptee and translates requests and responses between the two models.
Java example in the Order Management domain
package org.javaomnibus.ecommerce.gof.structural;
public interface ShipmentTracker {
ShipmentStatus track(String shipmentId);
}
public final class LegacyCarrierClient {
public String lookup(String consignmentNumber) {
return "IN_TRANSIT";
}
}
public final class LegacyCarrierAdapter implements ShipmentTracker {
private final LegacyCarrierClient legacyCarrierClient;
public LegacyCarrierAdapter(LegacyCarrierClient legacyCarrierClient) {
this.legacyCarrierClient = legacyCarrierClient;
}
@Override
public ShipmentStatus track(String shipmentId) {
return ShipmentStatus.valueOf(legacyCarrierClient.lookup(shipmentId));
}
}
Step by step
- The rest of the system depends on ShipmentTracker rather than the legacy client directly.
- The adapter isolates shape mismatch at one boundary point.
- That keeps the domain model from leaking legacy naming and return formats everywhere.
- Adapter is strongest when incompatibility is the real force rather than general simplification.
Where this pattern helps
- When integrating legacy libraries or external APIs with incompatible interfaces
- When you want boundary translation to stay out of application services and domain code
When a simpler design is better
- When you actually need to simplify a subsystem rather than translate one interface into another
- When adding one more wrapper would hide an already confused integration design
What this pattern costs
- Adds another layer to maintain at the boundary
- Can hide deeper integration problems if used as a patch over unstable upstream design
How this fits today
Adapter remains central in Java because most production systems integrate third-party SDKs, vendor APIs, or internal legacy modules that do not fit the domain model cleanly.
Where teams get it wrong
Calling any wrapper an adapter is misleading when the real role is simplification, access control, caching, or lazy loading rather than interface translation.