Patrón Adapter
Parte de la sección Programación Orientada a Objetos del Journey de C# de Coddy — lección 57 de 70.
El patrón Adapter es un patrón estructural que permite que interfaces incompatibles trabajen juntas. Actúa como un puente entre dos clases, convirtiendo la interfaz de una clase en una interfaz que los clientes esperan. Piense en ello como un adaptador de corriente que le permite enchufar un dispositivo europeo en un toma de corriente americano.
Este patrón es particularmente útil cuando quieres usar una clase existente pero su interfaz no coincide con lo que tu código necesita. En lugar de modificar la clase original (la cual podrías no controlar), creas un adaptador que la envuelve:
// Clase existente con interfaz incompatible
public class OldPrinter
{
public void PrintMessage(string msg) => Console.WriteLine($"Old: {msg}");
}
// Interfaz que su código espera
public interface IPrinter
{
void Print(string text);
}
// El adaptador cierra la brecha
public class PrinterAdapter : IPrinter
{
private OldPrinter _oldPrinter;
public PrinterAdapter(OldPrinter oldPrinter)
{
_oldPrinter = oldPrinter;
}
public void Print(string text)
{
_oldPrinter.PrintMessage(text); // Delega a la interfaz antigua
}
}Ahora su código puede funcionar con la impresora antigua a través de la interfaz esperada:
OldPrinter legacy = new OldPrinter();
IPrinter printer = new PrinterAdapter(legacy);
printer.Print("Hello"); // Anterior: HelloEl patrón Adapter es invaluable al integrar librerías de terceros o código legado en tu aplicación. El código del cliente trabaja con la interfaz IPrinter, sin saber en absoluto que una clase antigua e incompatible está realizando el trabajo real entre bastidores.
Desafío
FácilVamos a construir un sistema de reproductor de medios utilizando el patrón Adapter. Tienes una biblioteca de audio existente que reproduce archivos MP3, pero tu aplicación necesita una interfaz unificada que pueda manejar diferentes tipos de medios. Crearás un adaptador para cerrar esta brecha, permitiendo que tu reproductor de medios moderno funcione sin problemas con la biblioteca de audio heredada.
Organizarás tu código en tres archivos:
LegacyAudio.cs: Esto representa una biblioteca existente que no puedes modificar. Crea una claseLegacyAudioPlayeren el espacio de nombresMediaSystemcon un métodoPlayMp3(string filename)que devuelva la cadena"Playing MP3: {filename}". Este es tu "adaptee": la clase con la interfaz incompatible.MediaAdapter.cs: Crea la interfaz y el adaptador en el mismo espacio de nombres. Define una interfazIMediaPlayercon un métodoPlay(string filename)que devuelva una cadena. Luego, crea una claseMediaPlayerAdapterque implementeIMediaPlayer. Tu adaptador debe envolver una instancia deLegacyAudioPlayer(pasada a través del constructor) y traducir la llamadaPlayal métodoPlayMp3del reproductor heredado.Program.cs: Une todo creando el reproductor heredado, envolviéndolo en tu adaptador y utilizando el adaptador a través de la interfazIMediaPlayer. Esto demuestra cómo el código del cliente puede trabajar con la interfaz moderna mientras el adaptador se encarga de la comunicación con el sistema heredado entre bastidores.
Recibirás una entrada:
- El nombre del archivo a reproducir (por ejemplo,
song.mp3)
Crea un LegacyAudioPlayer, envuélvelo en un MediaPlayerAdapter, luego llama a Play en el adaptador utilizando la interfaz IMediaPlayer e imprime el resultado.
Por ejemplo, si la entrada es favorite_track.mp3, la salida debería ser:
Playing MP3: favorite_track.mp3La belleza de este patrón es que tu código principal solo interactúa con IMediaPlayer; no tiene idea de que una biblioteca de audio heredada está haciendo el trabajo real. Si más adelante necesitas dar soporte a una biblioteca heredada diferente, ¡simplemente crearías un nuevo adaptador sin cambiar nada del código del cliente!
Hoja de referencia
El patrón Adapter es un patrón estructural que permite que interfaces incompatibles trabajen juntas actuando como un puente entre dos clases.
El patrón convierte la interfaz de una clase en una interfaz que los clientes esperan, lo cual es útil cuando se desea utilizar una clase existente pero su interfaz no coincide con las necesidades de su código.
Estructura básica:
// Clase existente con interfaz incompatible (Adaptee)
public class OldPrinter
{
public void PrintMessage(string msg) => Console.WriteLine($"Old: {msg}");
}
// Interfaz objetivo que su código espera
public interface IPrinter
{
void Print(string text);
}
// El adaptador cierra la brecha
public class PrinterAdapter : IPrinter
{
private OldPrinter _oldPrinter;
public PrinterAdapter(OldPrinter oldPrinter)
{
_oldPrinter = oldPrinter;
}
public void Print(string text)
{
_oldPrinter.PrintMessage(text); // Delega a la interfaz antigua
}
}Uso:
OldPrinter legacy = new OldPrinter();
IPrinter printer = new PrinterAdapter(legacy);
printer.Print("Hello"); // Old: HelloEl código del cliente trabaja con la interfaz objetivo, sin saber que una clase incompatible está realizando el trabajo real entre bastidores. Este patrón es particularmente valioso al integrar librerías de terceros o código heredado.
Pruébalo tú mismo
using System;
using MediaSystem;
class Program
{
public static void Main(string[] args)
{
// Lee la entrada del nombre de archivo
string filename = Console.ReadLine();
// TODO: Crea una instancia de LegacyAudioPlayer
// TODO: Envuélvelo en un MediaPlayerAdapter
// TODO: Usa el adaptador a través de la interfaz IMediaPlayer
// e imprime el resultado de llamar a Play con el nombre de archivo
}
}
Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Programación Orientada a Objetos
1Fundamentos de OOP
Archivos externosNamespaces y directivasIntro a clases y objetosLa palabra clave 'this'Métodos y parámetrosCampos vs PropiedadesConstructoresInicializadores de objetosResumen - Calculadora simple4Herencia
Sintaxis básica de herencia (:)La palabra clave 'base'Palabras clave Virtual y OverrideClases selladas (Sealed)La clase base 'object'Resumen: Jerarquía de empleados7Características avanzadas
Sobrecarga de operadoresIndexadores (this[])Sobrescritura de ToString()Métodos de extensiónResumen - Lista personalizada10Patrones de diseño - Parte 1
Introducción a los patrones de diseñoSingleton Thread-SafePatrón FactoryPatrón Observer (Eventos)Patrón Strategy2Propiedades y miembros estáticos
Propiedades autoimplementadasPropiedades de solo lectura y escrituraCampos y métodos estáticosClases estáticasMiembros con cuerpo de expresión5Polimorfismo e Interfaces
Polimorfismo: Compilación vs. EjecuciónInterfaz vs. Clase AbstractaInterfaces MúltiplesInterfaces ExplícitasUpcasting y DowncastingRepaso: Calculadora de Figuras8Conceptos avanzados de POO
Composición sobre herenciaGenéricos (clases y métodos)Delegados y eventosAtributos y ReflexiónIDisposable y la sentencia usingFundamentos de Inyección de dependencias11Patrones de diseño, parte 2
Patrón CommandPatrón AdapterPatrón DecoratorPatrón Template MethodPatrón StatePatrón Composite3Arquitectura de clases
Datos de instancia vs. estáticosPalabras clave 'readonly' y 'const'Campos de respaldoResumen - Gestor de cuentas bancarias6Encapsulamiento
Modificadores de accesoPropiedades para el encapsulamientoImplementación de ocultamiento de datosPatrones de inmutabilidadRepaso - Registros de estudiantes