desarrollo

El patrón de diseño Adapter

Javier Sanchez ToledanoJavier Sanchez Toledano Twitter
El patrón de diseño Adapter
Comparte

El patrón de diseño Adapter que veremos a continuación, es el primero de los patrones de diseño de estructura, que se enfocan en la organización y composición de los objetos y clases en una aplicación. Estos patrones ayudan a resolver problemas relacionados con la estructura de las clases y objetos y su relación entre ellos.

Los patrones de estructura proporcionan una serie de soluciones probadas y prácticas para problemas comunes de diseño, y se centran en cómo los objetos interactúan y se relacionan entre sí para formar una estructura coherente. Esto significa que los patrones de estructura se utilizan para proporcionar una estructura a las aplicaciones y para asegurar que los objetos se relacionen de manera adecuada.

Los patrones de estructura pueden ayudar a mejorar la eficiencia, la escalabilidad y la capacidad de mantenimiento de una aplicación. Además, al utilizar estos patrones, se puede mejorar la legibilidad y la comprensión del código, ya que estos patrones son comunes en la programación orientada a objetos y su uso es conocido por muchos desarrolladores.

El patrón de diseño Adapter

El patrón de diseño Adapter se utiliza para convertir una interfaz existente en otra interfaz que el cliente espera. Este patrón se utiliza cuando una clase no puede interactuar directamente con otra clase debido a que tienen interfaces incompatibles. En lugar de modificar las clases existentes para que se ajusten a la interfaz deseada, se crea una clase adaptadora que actúa como intermediario entre las dos clases.

El adaptador encapsula un objeto existente y le proporciona una nueva interfaz para interactuar con otros objetos. Esto se logra mediante la creación de una clase que implementa la interfaz deseada y que contiene una referencia al objeto existente. La clase adaptadora se encarga de traducir las llamadas a la interfaz deseada a llamadas al objeto existente.

Las ventajas del patrón Adapter incluyen:

  • Flexibilidad
    El patrón Adapter permite que las clases existentes funcionen con otras clases sin modificar su código. Esto significa que se pueden utilizar objetos existentes con interfaces incompatibles en nuevas aplicaciones.
  • Reutilización
    El patrón Adapter permite reutilizar objetos existentes en lugar de crear nuevos objetos desde cero.
  • Separación de preocupaciones
    El patrón Adapter permite separar la funcionalidad de las clases existentes de la funcionalidad de la nueva interfaz. Esto facilita la organización y el mantenimiento del código.

En general, el patrón Adapter es conveniente utilizarlo cuando se necesita utilizar una clase existente que no tiene la interfaz requerida para interactuar con otras clases. Esto puede ocurrir cuando se está trabajando con código heredado, bibliotecas de terceros o cuando se está integrando sistemas de diferentes proveedores.

A continuación se muestra un diagrama de clase que representa el patrón Adapter:

+---------------+             +----------------+             +-------------------+
|   Client      |             |  Target        |             |   Adaptee         |
+---------------+             +----------------+             +-------------------+
|               |             |  request()     |             | specificRequest() |
|               +------------>|----------------|<------------+                   |
|               |             |                |             |                   |
+---------------+             +----------------+             +-------------------+
                                          ^                                      
                                          |                                      
                                 +--------------+                                 
                                 |  Adapter     |                                 
                                 +--------------+                                 
                                 |              |                                 
                                 |  request()   |                                 
                                 |              |                                 
                                 |              |                                 
                                 |  adaptee     |                                 
                                 +--------------+                                 

En este diagrama, el Client utiliza la interfaz Target para interactuar con el sistema. La clase Adaptee es una clase existente que no puede ser utilizada por el Client debido a que tiene una interfaz incompatible. La clase Adapter actúa como intermediario entre el Client y el Adaptee y adapta la interfaz del Adaptee a la interfaz Target que espera el Client. De esta manera, el Client puede interactuar con el Adaptee sin conocer los detalles de su implementación.

Ejemplos del patrón Adapter

Java

Supongamos que tenemos una interfaz Target que define un método request() que queremos utilizar en nuestra aplicación, pero tenemos una clase existente Adaptee que tiene una funcionalidad similar pero su método se llama specificRequest(). En lugar de modificar la interfaz existente Target, podemos crear un adaptador que implemente Target y haga uso de Adaptee para proporcionar la funcionalidad necesaria.

Primero, aquí está la interfaz Target:

public interface Target {
    void request();
}

A continuación, tenemos la clase Adaptee que proporciona una funcionalidad similar a la que queremos utilizar en nuestra aplicación:

public class Adaptee {
    public void specificRequest() {
        System.out.println("Adaptee specific request");
    }
}

Ahora podemos crear nuestro adaptador Adapter que implementa la interfaz Target y utiliza Adaptee para proporcionar la funcionalidad necesaria:

public class Adapter implements Target {
    private final Adaptee adaptee;

    public Adapter(Adaptee adaptee) {
        this.adaptee = adaptee;
    }

    @Override
    public void request() {
        adaptee.specificRequest();
    }
}

Finalmente, podemos utilizar nuestro adaptador en nuestra aplicación:

public class Client {
    public static void main(String[] args) {
        Adaptee adaptee = new Adaptee();
        Target target = new Adapter(adaptee);
        target.request();
    }
}

En este ejemplo, el adaptador Adapter permite que la clase Adaptee se utilice a través de la interfaz Target sin modificar la interfaz existente Target. De esta manera, podemos utilizar la funcionalidad existente de Adaptee en nuestra aplicación sin tener que modificar el código existente.

Es importante destacar que este es solo un ejemplo básico del patrón Adapter en Java, y que en aplicaciones más complejas se pueden encontrar implementaciones más sofisticadas del mismo.

Python

En este ejemplo en Python vemos el uso del patrón Adapter en un reproductor multimedia que necesita ser adaptado para reproducir archivos MP3:

# Clase de la interfaz MediaPlayer
class MediaPlayer:
    def play(self, audio_type, file_name):
        pass

# Clase de la interfaz AdvancedMediaPlayer
class AdvancedMediaPlayer:
    def play_vlc(self, file_name):
        pass

    def play_mp4(self, file_name):
        pass

# Clase de adaptador para AdvancedMediaPlayer
class MediaAdapter(MediaPlayer):
    def __init__(self, audio_type):
        if audio_type == 'vlc':
            self.advanced_music_player = VlcPlayer()
        elif audio_type == 'mp4':
            self.advanced_music_player = Mp4Player()

    def play(self, audio_type, file_name):
        if audio_type == 'vlc':
            self.advanced_music_player.play_vlc(file_name)
        elif audio_type == 'mp4':
            self.advanced_music_player.play_mp4(file_name)

# Clase VlcPlayer
class VlcPlayer(AdvancedMediaPlayer):
    def play_vlc(self, file_name):
        print("Playing vlc file. Name: " + file_name)

    def play_mp4(self, file_name):
        pass

# Clase Mp4Player
class Mp4Player(AdvancedMediaPlayer):
    def play_vlc(self, file_name):
        pass

    def play_mp4(self, file_name):
        print("Playing mp4 file. Name: " + file_name)

# Clase AudioPlayer que usa el adaptador MediaAdapter
class AudioPlayer(MediaPlayer):
    def play(self, audio_type, file_name):
        if audio_type == 'mp3':
            print("Playing mp3 file. Name: " + file_name)
        elif audio_type == 'vlc' or audio_type == 'mp4':
            adapter = MediaAdapter(audio_type)
            adapter.play(audio_type, file_name)
        else:
            print("Invalid media. " + audio_type + " format not supported")

# Ejemplo de uso
audio_player = AudioPlayer()

audio_player.play("mp3", "song.mp3")
audio_player.play("mp4", "movie.mp4")
audio_player.play("vlc", "video.vlc")
audio_player.play("avi", "video.avi")

En este ejemplo, la clase AudioPlayer representa el cliente y necesita reproducir archivos de música en diferentes formatos, incluido el formato MP3. Sin embargo, solo puede reproducir archivos de música en formato MP3 directamente. Para reproducir otros formatos, utiliza la clase MediaAdapter como adaptador para acceder a la funcionalidad proporcionada por las clases VlcPlayer y Mp4Player que implementan la interfaz AdvancedMediaPlayer. La clase MediaAdapter actúa como un puente entre la interfaz MediaPlayer y la interfaz AdvancedMediaPlayer para que el cliente AudioPlayer pueda reproducir archivos de música en diferentes formatos sin necesidad de conocer los detalles internos de las clases VlcPlayer y Mp4Player.

En resumen, el patrón Adapter es útil cuando se necesita adaptar una interfaz existente a otra interfaz compatible que no puede ser modificada o cuando se desea utilizar una clase existente que no es compatible con una interfaz requerida. Esto proporciona mayor flexibilidad y reutilización de código.

Javier Sanchez Toledano
Escrito por Javier Sanchez Toledano Sígueme

Soy Licenciado en Informática e Ingeniero en Sistemas Computacionales.
Soy auditor líder certificado por ICA en la Norma ISO 9000, desarrollo sistemas de gestión de la calidad con un enfoque de mejora continua, creo tableros de control con indicadores clave para mejorar la toma de decisiones basadas en datos.

Comentarios