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
- Interfície Target (
EnchufeEuropeu
): Defineix la interfície esperada pel client. - Classe Adaptee (
EnchufeAmericano
): Conté la interfície incompatible que volem adaptar. - Classe Adapter (
AdapterEnchufe
): Implementa la interfícieEnchufeEuropeu
i utilitza una instància d'EnchufeAmericano
per delegar la crida al mètodeplugIn()
. - Client: Utilitza l'Adapter per treballar amb
EnchufeAmericano
com si fos unEnchufeEuropeu
.
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
- Interfície Target (
ReproductorMusica
): Defineix la interfície esperada pel client. - Classe Adaptee (
ReproductorMP3
): Conté la interfície incompatible que volem adaptar. - Classe Adapter (
AdapterReproductor
): Implementa la interfícieReproductorMusica
i utilitza una instància deReproductorMP3
per delegar la crida al mètodeplayMP3()
. - Client: Utilitza l'Adapter per treballar amb
ReproductorMP3
com si fos unReproductorMusica
.
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.
Curs de Patrons de Disseny de Programari
Mòdul 1: Introducció als Patrons de Disseny
- Què són els Patrons de Disseny?
- Història i Origen dels Patrons de Disseny
- Classificació dels Patrons de Disseny
- Avantatges i Desavantatges d'Usar Patrons de Disseny
Mòdul 2: Patrons Creacionals
Mòdul 3: Patrons Estructurals
Mòdul 4: Patrons de Comportament
- Introducció als Patrons de Comportament
- Chain of Responsibility
- Command
- Interpreter
- Iterator
- Mediator
- Memento
- Observer
- State
- Strategy
- Template Method
- Visitor
Mòdul 5: Aplicació de Patrons de Disseny
- Com Seleccionar el Patró Adequat
- Exemples Pràctics d'Ús de Patrons
- Patrons de Disseny en Projectes Reals
- Refactorització Usant Patrons de Disseny
Mòdul 6: Patrons de Disseny Avançats
- Patrons de Disseny en Arquitectures Modernes
- Patrons de Disseny en Microserveis
- Patrons de Disseny en Sistemes Distribuïts
- Patrons de Disseny en Desenvolupament Àgil