El Adapter es un objeto que traduce una interfaz a otra que el cliente sí entiende. Permite integrar una clase existente sin modificarla ni contaminar el resto del sistema con sus particularidades.
Imagina que viajas a Estados Unidos con un portátil comprado en España. El cargador tiene dos patillas redondas, mientras que la toma del hotel espera patillas planas.
¿Qué hacéis? ¿Desmontáis la pared del hotel para cambiar el enchufe? No. ¿Cortáis el cable de vuestro cargador para empalmar uno americano? Espero que no.
Lo que hacéis es usar un Adaptador. Una pieza intermedia que recibe vuestras patillas redondas y saca patillas planas. El portátil no sabe que está en EE.UU., y la pared no sabe que le han enchufado un aparato europeo.
En software, el patrón Adapter (también conocido como Wrapper) hace exactamente lo mismo: permite que objetos con interfaces incompatibles colaboren.
El Problema: El sistema Legacy
Este escenario es un clásico. Estamos construyendo una aplicación moderna que consume datos en formato JSON.
Tenemos definida una interfaz que nuestro sistema entiende:
public interface INotificador
{
void EnviarMensaje(string json);
}Todo va bien hasta que nos dicen que tenemos que integrar una librería de terceros (o un sistema antiguo de la empresa) para enviar SMS. Pero esa librería es vieja y solo entiende XML.
// Clase de terceros (No la podemos modificar)
public class ServicioSMSAntiguo
{
public void EnviarSMS_XML(string xmlData)
{
Console.WriteLine($"Enviando XML: {xmlData}");
}
}Tenemos un problema: nuestro sistema habla JSON (EnviarMensaje), pero el servicio externo espera XML (EnviarSMS_XML). Son incompatibles. No podemos cambiar la librería externa (no tenemos el código o es de terceros) y no queremos ensuciar nuestro código moderno con lógica de XML.
La Solución: El Wrapper
Creamos una clase intermedia, el Adapter. Esta clase:
Implementa nuestra interfaz moderna (INotificador).
Contiene una instancia del servicio antiguo (ServicioSMSAntiguo).
En su método, traduce los datos de JSON a XML y llama al servicio antiguo.
:::
Implementación en C#
Vamos a construir ese adaptador para conectar nuestro sistema JSON con el servicio XML.
// El Adaptador implementa la interfaz que espera NUESTRO sistema (INotificador)
public class AdaptadorSMS : INotificador
{
// Mantenemos una referencia al objeto incompatible (Composición)
private readonly ServicioSMSAntiguo _servicioAntiguo;
public AdaptadorSMS(ServicioSMSAntiguo servicioAntiguo)
{
_servicioAntiguo = servicioAntiguo;
}
// Traducimos la llamada
public void EnviarMensaje(string mensajeJson)
{
// 1. Convertir los datos (la adaptación en sí)
string mensajeXml = ConvertirJsonAXml(mensajeJson);
// 2. Llamar al servicio legado
Console.WriteLine("Adaptador: Convirtiendo datos y delegando...");
_servicioAntiguo.EnviarSMS_XML(mensajeXml);
}
private string ConvertirJsonAXml(string json)
{
// Lógica de conversión (simulada)
return $"<msg>{json}</msg>";
}
}Fíjate que el cliente no tiene ni idea de que existe XML ni librerías antiguas. Él solo ve INotificador.
public class Cliente
{
public void ProcesarNotificaciones(INotificador notificador)
{
string miJson = "{ 'texto': 'Hola Mundo' }";
notificador.EnviarMensaje(miJson);
}
}class Program
{
static void Main(string[] args)
{
// Tenemos el servicio incompatible
ServicioSMSAntiguo servicioViejo = new ServicioSMSAntiguo();
// Creamos el adaptador envolviendo al servicio viejo
INotificador adaptador = new AdaptadorSMS(servicioViejo);
// El cliente usa el adaptador como si fuera cualquier otro notificador
Cliente cliente = new Cliente();
cliente.ProcesarNotificaciones(adaptador);
}
}Salida:
Adaptador: Convirtiendo datos y delegando...
Enviando XML: <msg>{ 'texto': 'Hola Mundo' }</msg>Tipos de Adaptadores
Existen dos variantes de este patrón, aunque en C# casi siempre usamos la primera:
- Adaptador de Objeto (El que hemos visto): Usa la Composición. El adaptador contiene una instancia de la clase a adaptar. Es el más flexible y el recomendado.
- Adaptador de Clase: Usa la Herencia. El adaptador hereda a la vez del Target y del Adaptee.
- Problema: C# (y Java) no soporta herencia múltiple de clases. Solo se podría hacer si
Targetfuera una interfaz (que lo es) yAdapteeuna clase. Aún así, acopla más el código.
Siempre que podáis, preferid la Composición sobre la Herencia. El Adaptador de Objeto es más seguro y permite adaptar subclases del Adaptee si fuera necesario.
¿Cuándo usar Adapter?
- Integración de Legacy: Cuando queréis usar una clase existente pero su interfaz no coincide con el resto de vuestro código.
- Librerías de Terceros: Para no acoplar vuestro dominio a una librería externa. Si mañana cambiáis la librería de SMS por otra, solo tenéis que crear un nuevo Adaptador, sin tocar el resto del código.
- Unificación: Cuando tenéis varias clases con funcionalidades similares pero interfaces distintas, y queréis procesarlas de forma polimórfica.