Patrones de Diseño (VII): Patrones Estructurales - Adapter

Una vez vistos y entendidos los patrones de creación pasaremos a ver los patrones estructurales, cuyo objetivo será gestionar cómo se combinan clases y objetos para dar lugar a estructuras más complejas.

Al igual que en los patrones de creación los patrones estructurales se dividen en patrones orientados a clases y patrones orientados a objetos:

  • De clase: Emplea interfaces para combinar clases incompatibles.
    • Patrones: Adaptador (de clases).
  • De objeto: Utiliza objetos para la combinación de estructuras.
    •  Patrones: Adaptador (de objetos), Puente, Composición, Decorador, Fachada, Flyweight y Proxy.

Como podemos apreciar existe un patrón Adapter orientados a clases y otro orientado a objetos. Se explicarán conjuntamente ya que su objetivo es el mismo, pero como ejemplo sólo veremos el Adapter orientado a los objetos ya que el orientado a clases requiere de herencia múltiple, característica que de la que prescinden los principales lenguajes de programación.

Dejemos la teoría de lado y pasemos a ver la plantilla que venimos usando en las anteriores entregas:

Adaptador


Nombre: Adapter (o Wrapper)

Problema y Contexto:

Necesitamos que un conjunto de clases con interfaces incompatibles sean capaces de cooperar entre si.

Se aplica cuando:

  • Pretendemos usar una clase existente cuya interfaz no es compatible con la requerida.
  • Tenemos que implementar una clase que pueda ser reusable y que coopere con clases no relacionadas, es decir, las clases que no tienen necesariamente interfaces compatibles.


Solución y Estructura:

Class Adapter:

Esta clase de adaptadores es menos utilizada poque utiliza herencia múltiple. Crearemos una clase adaptador que herede de la clase o clases a adaptar, cuyos métodos pueden ser sobreescritos. Además el adaptador deberá implementar la nueva interfaz que deseamos utilizar. La adatación se realiza llamando a los métodos del padre. No permite la adaptación de las subclases de la clase adaptada.

Object Adapter:

Creamos una clase adaptadora que contiene una instancia de la clase o clases a adaptar. Para compatibilizar las clases, el adaptador hace llamadas a la instancia del objeto u objetos a adaptar. Este adaptador funciona con la clase adaptada y todas sus subclases.

La estructura es la siguiente:

Class Adapter:

http://i.stack.imgur.com/ZLTBJ.png

Object Adapter:




Donde:

Adaptee: Se trata de la clase adaptada (o a adaptar). Se trata de una clase existente la cual pretendemos adaptar para que funcione bajo una nueva interfaz. Puede haber varios Adaptees.

Target: Nueva interfaz utilizada por Client a la que debemos adaptar a Adaptee.

Adaptor: Clase que implementará la interfaz Target y se encargará de que la antigua funcionalidad obedezca a la misma.

Client: Utiliza la funcionalidad de Adaptee de acuerdo a la interfaz Target implementada por Adaptor.

Consecuencias:

  • POSITIVAS:
    • Permite adaptar clases en dominios totalmente diferentes.
    • Un único adaptador puede adaptar la funcionalidad de múltiples clases.
  • NEGATIVAS:
    • Dependiendo de la implementación, el adaptador puede contener múltiples punteros que incrementen la complejidad del sistema.

Patrones Relacionados: Bridge, Decorator, Facade y Proxy

Ejemplo:

Utilizaremos un ejemplo sencillo para entender bien la funcionalidad: Queremos adaptar una interfaz HDMI para que se vea en televisiones con RCA. En primer lugar definiremos la clase adaptada (Adaptee):

public class HDMI{
public HDMI(){
System.out.println("Nuevo conector HDMI creado.");
}

public void obtenerVideoHD(){
System.out.println("Video obtenido desde fuente HD.");
}

public void obtenerAudioHD(){
System.out.println("Audio obtenido desde fuente HD.");
}
}

Continuaremos definiendo la interfaz que necesitamos en nuestro sistema actual (Target):

public interface RCA{
public void obtenerCanalVideo();
public void obtenerCanalAudioDcho();
public void obtenerCanalAudioIzdo();
}


A continuación definiremos el adaptador (Adapter). Crearemos 2 adaptadores para ilustrar los 2 tipos de adaptadores (clases y objetos):


// Adaptador de Clases
public class HDMItoRCAClassAdapter extends HDMI implements RCA{

public void obtenerCanalVideo(){
extraerVideo();
}

public void obtenerCanalAudioDcho(){
extraerAudioDcho();
}

public void obtenerCanalAudioIzdo(){
extraerAudioIzdo();
}

private extraerVideo(){
obtenerVideoHD();
System.out.println("Convertir fuente de video HD a Video analógico");
}

private extraerAudioDcho(){
obtenerAudioHD();
System.out.println("Extrayendo canal Derecho de Audio");
System.out.println("Convertir fuente de audio HD a Audio analógico Derecho");
}

private extraerAudioIzdo(){
obtenerAudioHD();
System.out.println("Extrayendo canal Izquierdo de Audio");
System.out.println("Convertir fuente de audio HD a Audio analógico Izquierdo");
}
}

// Adaptador de Objetos
public class HDMItoRCAObjectAdapter implements RCA{

private HDMI fuente;

public HDMItoRCAObjectAdapter(){
fuente = new HDMI();
}

public void obtenerCanalVideo(){
extraerVideo();
}

public void obtenerCanalAudioDcho(){
extraerAudioDcho();
}

public void obtenerCanalAudioIzdo(){
extraerAudioIzdo();
}

private extraerVideo(){
fuente.obtenerVideoHD();
System.out.println("Convertir fuente de video HD a Video analógico");
}

private extraerAudioDcho(){
fuente.obtenerAudioHD();
System.out.println("Extrayendo canal Derecho de Audio");
System.out.println("Convertir fuente de audio HD a Audio analógico Derecho");
}

private extraerAudioIzdo(){
fuente.obtenerAudioHD();
System.out.println("Extrayendo canal Izquierdo de Audio");
System.out.println("Convertir fuente de audio HD a Audio analógico Izquierdo");
}
}

Por último queda definir un cliente que utilice el adaptador, que lo simplificaremos mostrando únicamente el metodo main():

public static void main(String args){

RCA conector;

if(args[0].equals("ClassAdapter")){
conector = new HDMItoRCAClassAdapter();
} else if(args[1].equals("ObjectAdapter")){
conector = new HDMItoRCAObjectAdapter();
}

// De esta manera conseguimos trabajar con video HDMI como si fuese analógico

conector.obtenerCanalVideo();
conector.obtenerCanalAudioDcho();
conector.obtenerCanalAudioIzdo();
}

COMPARTE ESTE ARTÍCULO

COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP