Adapter Pattern
Pattern
Adapter is a structural design pattern that allows objects with incompatible interfaces to collaborate.
Let's explain this pattern using an example form EN.601.226 Data Structures: one of the homeworks is about implementing a (supremely) simple search engine (using hash tables) called JHUgle
(cheeky! I know!!). The JHUgle
uses a Map
interface which is (slightly) different from Java's java.util.Map
:
Namely, there are two methods insert
and put
where insert
adds a new <key,value>
pair (it would throw exception if map already contains the key). The put
method on the other hand is used to update the value associated with a key (already inserted in the map). Java's Map
has only a put
method that convolves the behavior of our Map's insert
and put
.
To use Java's Map in JHUgle
app, you can create an adapter for it.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | public class JDKMapAdapter<K, V> implements Map<K, V> { private java.util.Map<K, V> map = new java.util.HashMap<>(); @Override public void insert(K k, V v) throws IllegalArgumentException { if (k == null || map.containsKey(k)) { throw new IllegalArgumentException(); } map.put(k, v); } @Override public void put(K k, V v) throws IllegalArgumentException { if (k == null || !map.containsKey(k)) { throw new IllegalArgumentException(); } map.put(k, v); } // Implementation of other methods are omitted to save space. } |
As an aside, also note this pattern follows the Dependency Inversion Principle.
The general idea of an adapter class is similar to adapter in the physical world: think of a mobile charger as an adapter; the mobile battery needs 3 volts to charge but the normal socket produces either 120V (US) or 220V (Europe). So the mobile charger works as an adapter between the mobile charging socket and the wall socket.
When to use this pattern?
Use the Adapter when you want to use some existing class, but its interface isn't compatible with the rest of your code.
Advantage
You don't need to change the existing class or the interface. By introducing a new class, which acts as an adapter between the interface and the existing class, you reuse your existing code without changing it.