Introducció al Patró Adapter

El patró Adapter és un patró estructural que permet que classes amb interfícies incompatibles treballin juntes. Aquest patró converteix la interfície d'una classe en una altra interfície que els clients esperen. Adapter permet que classes amb interfícies incompatibles col·laborin.

Objectius del Patró Adapter

  • Facilitar la compatibilitat entre interfícies incompatibles.
  • Permetre la reutilització de classes existents sense modificar el seu codi.
  • Millorar la flexibilitat i mantenibilitat del codi.

Estructura del Patró Adapter

El patró Adapter pot ser implementat de dues maneres: mitjançant l'herència (Adapter de classe) o mitjançant la composició (Adapter d'objecte).

Adapter de Classe

Aquest tipus d'Adapter utilitza l'herència per adaptar una interfície a una altra.

Diagrama UML:

+-----------------+
| Target          |
|-----------------|
| +request()      |
+-----------------+
        ^
        |
+-----------------+
| Adapter         |
|-----------------|
| +request()      |
|-----------------|
| -adaptee: Adaptee|
+-----------------+
        ^
        |
+-----------------+
| Adaptee         |
|-----------------|
| +specificRequest()|
+-----------------+

Adapter d'Objecte

Aquest tipus d'Adapter utilitza la composició per adaptar una interfície a una altra.

Diagrama UML:

+-----------------+
| Target          |
|-----------------|
| +request()      |
+-----------------+
        ^
        |
+-----------------+
| Adapter         |
|-----------------|
| +request()      |
|-----------------|
| -adaptee: Adaptee|
+-----------------+
        ^
        |
+-----------------+
| Adaptee         |
|-----------------|
| +specificRequest()|
+-----------------+

Exemple Pràctic

Problema

Suposem que tenim una classe EnchufeEuropeu que té un mètode conectar(). També tenim una classe EnchufeAmericano que té un mètode plugIn(). Volem utilitzar EnchufeAmericano en un context on s'espera un EnchufeEuropeu.

Solució

Implementarem un Adapter que permeti que EnchufeAmericano sigui utilitzat com un EnchufeEuropeu.

Codi

// Interfície Target
interface EnchufeEuropeu {
    void conectar();
}

// Classe Adaptee
class EnchufeAmericano {
    public void plugIn() {
        System.out.println("Enchufe Americano conectado.");
    }
}

// Classe Adapter
class AdapterEnchufe implements EnchufeEuropeu {
    private EnchufeAmericano enchufeAmericano;

    public AdapterEnchufe(EnchufeAmericano enchufeAmericano) {
        this.enchufeAmericano = enchufeAmericano;
    }

    @Override
    public void conectar() {
        enchufeAmericano.plugIn();
    }
}

// Client
public class Main {
    public static void main(String[] args) {
        EnchufeAmericano enchufeAmericano = new EnchufeAmericano();
        EnchufeEuropeu adapter = new AdapterEnchufe(enchufeAmericano);

        adapter.conectar(); // Sortida: Enchufe Americano conectado.
    }
}

Explicació del Codi

  1. Interfície Target (EnchufeEuropeu): Defineix la interfície esperada pel client.
  2. Classe Adaptee (EnchufeAmericano): Conté la interfície incompatible que volem adaptar.
  3. Classe Adapter (AdapterEnchufe): Implementa la interfície EnchufeEuropeu i utilitza una instància d'EnchufeAmericano per delegar la crida al mètode plugIn().
  4. Client: Utilitza l'Adapter per treballar amb EnchufeAmericano com si fos un EnchufeEuropeu.

Exercici Pràctic

Enunciat

Implementa un Adapter per permetre que una classe ReproductorMP3 amb un mètode playMP3() pugui ser utilitzada en un context on s'espera una interfície ReproductorMusica amb un mètode play().

Solució

// Interfície Target
interface ReproductorMusica {
    void play();
}

// Classe Adaptee
class ReproductorMP3 {
    public void playMP3() {
        System.out.println("Reproduint arxiu MP3.");
    }
}

// Classe Adapter
class AdapterReproductor implements ReproductorMusica {
    private ReproductorMP3 reproductorMP3;

    public AdapterReproductor(ReproductorMP3 reproductorMP3) {
        this.reproductorMP3 = reproductorMP3;
    }

    @Override
    public void play() {
        reproductorMP3.playMP3();
    }
}

// Client
public class Main {
    public static void main(String[] args) {
        ReproductorMP3 reproductorMP3 = new ReproductorMP3();
        ReproductorMusica adapter = new AdapterReproductor(reproductorMP3);

        adapter.play(); // Sortida: Reproduint arxiu MP3.
    }
}

Explicació del Codi

  1. Interfície Target (ReproductorMusica): Defineix la interfície esperada pel client.
  2. Classe Adaptee (ReproductorMP3): Conté la interfície incompatible que volem adaptar.
  3. Classe Adapter (AdapterReproductor): Implementa la interfície ReproductorMusica i utilitza una instància de ReproductorMP3 per delegar la crida al mètode playMP3().
  4. Client: Utilitza l'Adapter per treballar amb ReproductorMP3 com si fos un ReproductorMusica.

Errors Comuns i Consells

  • No oblidar la composició o herència: Assegura't d'utilitzar la composició o herència adequadament segons el tipus d'Adapter que estàs implementant.
  • Mantenir la interfície clara: L'Adapter ha de proporcionar una interfície clara i coherent per al client.
  • Evitar la complexitat innecessària: No afegeixis complexitat innecessària a l'Adapter. Mantingues el codi simple i fàcil de mantenir.

Conclusió

El patró Adapter és una eina poderosa per permetre que classes amb interfícies incompatibles treballin juntes. Mitjançant l'ús de l'herència o la composició, podem adaptar una interfície a una altra, facilitant la reutilització de codi i millorant la flexibilitat del sistema.

© Copyright 2024. Tots els drets reservats