patron-adapter

Patrón Adapter: conecta interfaces incompatibles

  • 4 min

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);
}
Copied!

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}");
    }
}
Copied!

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>";
    }
}
Copied!

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);
    }
}
Copied!
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);
    }
}
Copied!

Salida:

Adaptador: Convirtiendo datos y delegando...
Enviando XML: <msg>{ 'texto': 'Hola Mundo' }</msg>
Copied!

Tipos de Adaptadores

Existen dos variantes de este patrón, aunque en C# casi siempre usamos la primera:

  1. 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.
  2. 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 Target fuera una interfaz (que lo es) y Adaptee una 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.