Problema
Hacer que clases con interfaces incompatibles puedan trabajar juntas sin modificar su código fuente.
Propósito
Convertir la interfaz de una clase en otra interfaz que el cliente espera. Permite que clases con interfaces incompatibles colaboren, actuando como un puente entre el código existente y el nuevo.
Casos de uso comunes
- Integrar sistemas legacy con código moderno
- Adaptar bibliotecas de terceros a tu interfaz
- Conectar APIs con diferentes formatos de datos
- Reutilizar código existente sin modificarlo
¿Quién es quién en Adapter?
Actor | Lo que realmente es | Ejemplo | Analogía |
---|---|---|---|
Target | Interfaz que el cliente espera | PaymentProcessor - cómo quiere trabajar el cliente |
“Hablar en español” (interfaz esperada) |
Adapter | Traduce entre Target y Adaptee | PaymentAdapter - convierte llamadas modernas a legacy |
Traductor |
Adaptee | Clase existente con interfaz incompatible | LegacyPayment - funcionalidad útil pero diferente interfaz |
Guía local que solo habla inglés |
Client | Usa el servicio, solo conoce Target | Client - procesa órdenes usando PaymentProcessor |
Turista español |
Diagrama
classDiagram
namespace AdapterPattern {
class Target {
<<interface>>
+request()
}
class Adapter {
-adaptee: Adaptee
+request()
}
class Adaptee {
+specificRequest()
}
class Client {
+clientCode(Target)
}
}
Target <|.. Adapter
Adapter --> Adaptee
Client --> Target
Ejemplo práctico
classDiagram
namespace PaymentExample {
class Client {
+processOrder()
}
class PaymentProcessor {
<<interface>>
+processPayment(amount, card)
}
class PaymentAdapter {
-legacySystem: LegacyPayment
+processPayment(amount, card)
}
class LegacyPayment {
+makePayment(dollars, cardNumber)
}
}
Client --> PaymentProcessor
PaymentProcessor <|.. PaymentAdapter
PaymentAdapter --> LegacyPayment
Flujo de funcionamiento
sequenceDiagram
participant Client
participant PaymentAdapter
participant LegacyPayment
Client->>PaymentAdapter: processPayment(100, "1234-5678")
Note over PaymentAdapter: Traduce interfaz moderna a legacy
PaymentAdapter->>LegacyPayment: makePayment(100, "1234-5678")
LegacyPayment-->>PaymentAdapter: true
PaymentAdapter-->>Client: success
Ventajas
- Reutilización: Permite usar código existente sin modificarlo
- Desacoplamiento: Separa el cliente del sistema adaptado
- Flexibilidad: Fácil intercambio de implementaciones
- Integración: Conecta sistemas con interfaces diferentes
Desventajas
- Complejidad: Añade una capa adicional de abstracción
- Performance: Puede introducir overhead mínimo
- Mantenimiento: Cambios en el adaptee pueden requerir cambios en el adapter
Cuándo usar
- Necesitas usar una clase existente con interfaz incompatible
- Quieres integrar bibliotecas de terceros
- Tienes sistemas legacy que no puedes modificar
- Necesitas que clases no relacionadas trabajen juntas
Cuándo NO usar
- Las interfaces ya son compatibles
- Puedes modificar directamente las clases existentes
- La adaptación es demasiado compleja o forzada
- Solo necesitas cambios menores en la interfaz
Diferencias con otros patrones
- vs Facade: Adapter cambia interfaz, Facade simplifica interfaz compleja
- vs Decorator: Adapter cambia interfaz, Decorator añade funcionalidad
- vs Proxy: Adapter cambia interfaz, Proxy controla acceso con misma interfaz
- vs Bridge: Adapter conecta incompatibles, Bridge separa abstracción de implementación