Adapter
A thin wrapper that lets a class work with code that expects a different interface.
- Structural
- A USB drive used through a memory card interface
- Java code
The idea
Sometimes a class does what you need, but its methods don't match what your code expects. Your code calls readData(), and the class offers read(). You may not be able to change either side: one is a library, or the other is used in many places.
An adapter sits between them. It implements the interface your code expects, holds the other object, and turns each call into a call that object understands. It works like a travel plug adapter: neither the socket nor the plug changes.
The Gang of Four describe two forms. A class adapter inherits from the class it adapts. An object adapter, like the one in this example, holds a reference to it. The object form fits Java better, since a class can extend only one other class.
When to use it
- You want to use an existing class, but its interface doesn't match the one your code needs.
- You are adding a third-party library and want to keep its API out of the rest of your code.
- You are replacing one implementation with another that has a different interface, and don't want to change every caller.
- Several existing classes do similar work with different method names, and you want to treat them the same way.
Pros and cons
Pros
- You reuse existing classes without changing them.
- It separates concerns: the conversion lives in one class, away from the business logic.
- Client code depends only on its own interface, so you can swap the adapted class later.
Cons
- It adds a class and a layer of indirection for every interface you adapt.
- If the two interfaces differ a lot, the adapter can grow complex and hide real differences in behaviour.
The example
The example uses storage devices. The client works with memory cards through the MemoryCard interface. A flash drive offers the same kind of operations through a different interface, USBDrive. A card reader adapter lets the client use the flash drive as if it were a memory card.
MemoryCard: the target: the interface the client expects, withreadData()andwriteData().USBDrive: the interface of the class being adapted, withread()andwrite().SDMemoryCard: an ordinaryMemoryCardthat the client uses directly.FlashDrive: the adaptee: aUSBDrivethat the client cannot use as it is.USBCardReaderAdapter: the adapter. It implementsMemoryCard, holds aUSBDrive, and forwardsreadData()toread()andwriteData()towrite().Demo: the client. It uses the SD card and then the flash drive, both through theMemoryCardinterface.
The code
public interface MemoryCard {
void readData();
void writeData();
}
public interface USBDrive {
void read();
void write();
}
class SDMemoryCard implements MemoryCard {
public void readData() {
System.out.println("Reading data from SD memory card");
}
public void writeData() {
System.out.println("Writing data to SD memory card");
}
}
public class FlashDrive implements USBDrive {
public void read() {
System.out.println("Reading data from flash drive");
}
public void write() {
System.out.println("Writing data to flash drive");
}
}
public class USBCardReaderAdapter implements MemoryCard {
private final USBDrive flashDrive;
public USBCardReaderAdapter(USBDrive flashDrive) {
this.flashDrive = flashDrive;
}
public void readData() {
flashDrive.read();
}
public void writeData() {
flashDrive.write();
}
}
public class Demo {
public static void main(String[] args) {
MemoryCard sdCard = new SDMemoryCard();
sdCard.readData();
sdCard.writeData();
USBDrive flashDrive = new FlashDrive();
MemoryCard adapter = new USBCardReaderAdapter(flashDrive);
adapter.readData();
adapter.writeData();
}
}
Running Demo prints:
Reading data from SD memory card
Writing data to SD memory card
Reading data from flash drive
Writing data to flash drive