Sacas la tarjeta de memoria de tu celular, una microSD para ver su contenido en tu Laptop. Resulta que tu Laptop tiene una ranura para tarjetas SD. Que haces?. Pues consigues un adaptador de memorias y listo. Este ejemplo lo ves a diario con otras cosas como adaptadores de corriente de 3 pines a 2 pines.
En el software hay situaciones similares. Cuando decides incorporar algún componente nuevo pero tu aplicación no es compatible con la interface de ese nuevo componente. Entonces te preguntas qué hacer? Modificar el componente nuevo es imposible porque es una librería de tercero. Modificar tu aplicación para que trabaje con la interface del nuevo componente?. Es una opción pero no es lo adecuado.
Aquí es cuando aparece el patrón adaptador al rescate. Este patrón proporciona el puente entre estas dos interfaces incompatibles. Hace que el nuevo componente sea lo que tu aplicación espera. Es como un disfraz que se lo colocas a la nueva librería y asi tu software cree que es lo mismo que siempre el espera.
Patrón adaptador
Es un patrón estructural descrito por GoF que dice así:
“Convierte la interface de una clase en otra que los clientes esperan. Permite a las clases trabajar juntas que de otra forma no podrían por las interfaces incompatibles”.
Diagrama de Clases
Participantes
- Target : Define la interface especifica que el cliente usa.
- Adapter : Adapta la interface
Adaptee
a la interfaceTarget
. - Adaptee : La interface que necesita ser adaptada al
Cliente
. - Client : Colabora para usar
Target
.
Vamos a ver un ejemplo para estar más claros.
Tienes un clase Cliente
que recibe una lista para recorrer. Esta lista es una Enumeration
y posee métodos
particulares para recorrerla, estos son hasMoreElements()
y nextElement()
.
1
2
3
4
5
6
7
8
9
10
import java.util.Enumeration;
public class Client {
public void imprimirLista(Enumeration target){
while(target.hasMoreElements()){
//Aqui recorremos la enumeracion
}
}
}
Pero resulta que en la nueva librería, la lista usa la interface Iterator
que posee otro tipo de métodos para recorrerla,
hasNext()
y next()
.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import java.util.Iterator;
public class NewList implements Iterator {
public boolean hasNext() {
return true;
}
public Object next() {
return new Object();
}
public void remove() {
//algun código
}
}
Aqui el metodo imprimirLista()
de tu clase cliente no permite Iterator
si no Enumeration
. Así que tenemos que crear
el adaptador de Iterator
en Enumeration
que es la que espera tu clase cliente.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import java.util.Enumeration;
import java.util.Iterator;
public class IteratorEnumerationAdapter implements Enumeration{
private Iterator iterator;
public IteratorEnumerationAdapter(Iterator iterator) {
this.iterator = iterator;
}
public boolean hasMoreElements() {
return iterator.hasNext();
}
public Object nextElement() {
return iterator.next();
}
}
Ahora veamos como queda el uso de esta estructura de clases.
1
2
3
4
5
6
7
8
9
10
public class Test {
public static void main(String[] args) {
Client client = new Client();
NewList adaptee = new NewList();
IteratorEnumerationAdapter adapter = new IteratorEnumerationAdapter(adaptee);
client.imprimirLista(adapter);
}
}
El Client
recibe felizmente el adaptador que usa internamente el nuevo tipo de lista.
Conclusión
Este patrón es muy sencillo, usa principios como la delegación y la composición. Se usa el polimorfismo en el Target
y
el Cliente
ni se entera que el objeto real es un Adaptador.