Compare class and object adapters in Java, with emphasis on why composition-based object adapters are usually the safer default.
The classic Adapter pattern has two variants:
In Java, object adapter is usually the better default.
A class adapter extends the adaptee and implements the target interface:
1public final class LegacyGatewayClassAdapter
2 extends LegacyGateway
3 implements PaymentProcessor {
4
5 @Override
6 public Receipt charge(Money amount, String token) {
7 LegacyResponse response =
8 makePayment(amount.toMinorUnits(), token);
9 return new Receipt(response.reference(), response.approved());
10 }
11}
This can be compact, but it comes with trade-offs:
An object adapter receives the adaptee as a collaborator:
1public final class LegacyGatewayObjectAdapter
2 implements PaymentProcessor {
3 private final LegacyGateway gateway;
4
5 public LegacyGatewayObjectAdapter(LegacyGateway gateway) {
6 this.gateway = gateway;
7 }
8
9 @Override
10 public Receipt charge(Money amount, String token) {
11 LegacyResponse response =
12 gateway.makePayment(amount.toMinorUnits(), token);
13 return new Receipt(response.reference(), response.approved());
14 }
15}
This fits Java better because composition is more flexible than inheritance and plays well with testing, DI, and alternate implementations.
Use a class adapter only when:
Use an object adapter in most application code.
If the adapter is part of an application boundary, prefer object adapter. If it is a narrow internal convenience wrapper and inheritance genuinely simplifies the design, class adapter may be acceptable.