Retomando de nuevo la serie de artículos sobre patrones de diseño, hoy vamos a ver el patrón Adapter. Este va a ser el primero de los patrones estructurales que vamos a ver. La definición dada por el libro es que este patrón nos permite convertir una interfaz de una clase en otra interfaz que el cliente está esperando. El patrón Adapter permite de esta forma trabajar juntas a clases que de otra forma no podrían por la incompatibilidad de sus interfaces. Básicamente, y en palabras mucho más llanas, vosotros lo conoceréis como Wrapper, que no es más que una envoltura sobre algún objeto en concreto para que podamos utilizarlo desde puntos donde no estaba pensado antes.
Con las lineas que se han explicado arriba deberíamos tenerlo ya claro, sobre todo, después de saber que este patrón es un wrapper, termino que casi cualquier desarrollador estoy seguro de que conoce. Pero de todas formas, vamos a entrar un poco en detalle. Este tipo de patrón se creo para intentar que objetos implementados a partir de diferentes interfaces pudieran trabajar juntos salvando esta diferencia de interfaces. De esta forma objetos que ya tenemos implementeados pueden ser adaptados para ser reutilizados sin que se necesiten cambios en ellos, simplemente envolviéndolos con una capa alrededor que nos permitirá adaptarlos al nuevo interfaz deseado. Pero como siempre, lo mas fácil es ver esto con un ejemplo, así que sigamos avanzando.
Este patrón se compone de los siguientes elementos:
- Target: Define el dominio específico de la interfaz que el cliente va a utilizar.
- Client: Maneja los diferentes objetos según le permite el interfaz Target.
- Adaptee: Define un interfaz existente que necesita ser adaptado.
- Adapter: Adapta el interfaz de Adaptee al interfaz usado por el cliente Target.
Al final, el comportamiento resimido de esto, será que el cliente llamará a métodos del interfaz Adapter que a su vez llamarán a métodos del objeto Adaptee.
Pero bueno, vamos a ver todo esto con un poco de código que seguro que nos lo deja algo más claro que una explicación. Vamos a implementar una mini aplicación que poner vehiculos de carreras a correr en un similador por ejemplo, y añadir un vehiculo que no es de carreras para hacer unas pruebas.
Target:
public interface VehiculoCarreras { public void correr(); ... }
Clases válidas:
public class CitroenCarreras extends vehiculoCarreras { public void correr() { ... } } public class OpelCarreras extends vehiculoCarreras { public void correr() { ... } }
Adaptee:
public class Renault { public void arranca() { ... } public void meterPrimera() { ... } public void acelerar() { ... } ... }
Adapter:
public class RenaultCarrerasWrapper extends VehiculoCarreras { public void correr() { ... renault.arranca(); renault.metePrimera(); renault.acelera(); ... } }
Client:
public class Main() { public static void main(String[] args) { VehiculoCarreras vc = new CitroenCarreras(); vc.correr(); vc = new OpelCarreras(); vc.correr(); // vc = new Renault(); No es posible hacer esto vc = new RenaultCarrerasWrapper(); vc.correr(); } }
Como podéis ver tenemos entre nuestros vehiculos de carreras un coche normal (Renault) porque queremos hacer una prueba con él, para no tener que tocar la implementación de este, hemos desarrollado un wrapper por encima (RenaultCarrerasWrapper) para que pueda ser manejado por nuestro programa.
Así de simple es este patrón. Como final, solo queda añadir que está relacionado con patrones como:
- Bridge: Tienen una estructura similar, pero el objetivo de Bridge es separar la interfaz de la implementación. Ya lo veremos próximamente.
- Decorator: Es otro camino para cambiar un objeto sin cambiar su interfaz, quizás este sea un poquito más transparente, lo cual le permite soportar composición recursiva, cosa que no es posible con Adapter.
- Proxy: Define una representación o sustituto de un objeto sin cambiar su interfaz. Ya lo veremos más adelante.
Bueno, hasta aquí todo. Espero que os sirva de ayuda y haberos dejado un poquito más claro este patrón. Nos vemos.