Padrão Adapter
Parte da seção Programação Orientada a Objetos do Journey de C# da Coddy — lição 57 de 70.
O padrão Adapter é um padrão estrutural que permite que interfaces incompatíveis trabalhem juntas. Ele age como uma ponte entre duas classes, convertendo a interface de uma classe em uma interface que os clientes esperam. Pense nele como um adaptador de tomada que permite conectar um dispositivo europeu em uma tomada americana.
Este padrão é particularmente útil quando você deseja usar uma classe existente, mas sua interface não corresponde ao que seu código precisa. Em vez de modificar a classe original (que você pode não controlar), você cria um adaptador que a envolve:
// Classe existente com interface incompatível
public class OldPrinter
{
public void PrintMessage(string msg) => Console.WriteLine($"Old: {msg}");
}
// Interface que seu código espera
public interface IPrinter
{
void Print(string text);
}
// O Adapter preenche a lacuna
public class PrinterAdapter : IPrinter
{
private OldPrinter _oldPrinter;
public PrinterAdapter(OldPrinter oldPrinter)
{
_oldPrinter = oldPrinter;
}
public void Print(string text)
{
_oldPrinter.PrintMessage(text); // Delega para a interface antiga
}
}Agora seu código pode funcionar com a impressora antiga através da interface esperada:
OldPrinter legacy = new OldPrinter();
IPrinter printer = new PrinterAdapter(legacy);
printer.Print("Hello"); // Antigo: HelloO padrão Adapter é inestimável ao integrar bibliotecas de terceiros ou código legado em sua aplicação. O código do cliente trabalha com a interface IPrinter, completamente alheio ao fato de que uma classe antiga e incompatível está realizando o trabalho real nos bastidores.
Desafio
FácilVamos construir um sistema de player de mídia usando o padrão Adapter. Você tem uma biblioteca de áudio existente que reproduz arquivos MP3, mas sua aplicação precisa de uma interface unificada que possa lidar com diferentes tipos de mídia. Você criará um adaptador para preencher essa lacuna, permitindo que seu player de mídia moderno funcione perfeitamente com a biblioteca de áudio legada.
Você organizará seu código em três arquivos:
LegacyAudio.cs: Isso representa uma biblioteca existente que você não pode modificar. Crie uma classeLegacyAudioPlayerno namespaceMediaSystemcom um métodoPlayMp3(string filename)que retorna a string"Playing MP3: {filename}". Este é o seu "adaptee" - a classe com a interface incompatível.MediaAdapter.cs: Crie a interface e o adaptador no mesmo namespace. Defina uma interfaceIMediaPlayercom um métodoPlay(string filename)que retorna uma string. Em seguida, crie uma classeMediaPlayerAdapterque implementaIMediaPlayer. Seu adaptador deve envolver uma instância deLegacyAudioPlayer(passada pelo construtor) e traduzir a chamadaPlaypara o métodoPlayMp3do player legado.Program.cs: Reúna tudo criando o player legado, envolvendo-o em seu adaptador e usando o adaptador através da interfaceIMediaPlayer. Isso demonstra como o código do cliente pode trabalhar com a interface moderna enquanto o adaptador cuida da comunicação com o sistema legado nos bastidores.
Você receberá uma entrada:
- O nome do arquivo para reproduzir (ex:
song.mp3)
Crie um LegacyAudioPlayer, envolva-o em um MediaPlayerAdapter, então chame Play no adaptador usando a interface IMediaPlayer e imprima o resultado.
Por exemplo, se a entrada for favorite_track.mp3, a saída deve ser:
Playing MP3: favorite_track.mp3A beleza deste padrão é que seu código principal interage apenas com IMediaPlayer - ele não tem ideia de que uma biblioteca de áudio legada está fazendo o trabalho real. Se mais tarde você precisar oferecer suporte a uma biblioteca legada diferente, basta criar um novo adaptador sem alterar nenhum código do cliente!
Folha de consulta
O padrão Adapter é um padrão estrutural que permite que interfaces incompatíveis trabalhem juntas, agindo como uma ponte entre duas classes.
O padrão converte a interface de uma classe em uma interface que os clientes esperam, sendo útil quando você deseja usar uma classe existente, mas sua interface não corresponde às necessidades do seu código.
Estrutura básica:
// Classe existente com interface incompatível (Adaptee)
public class OldPrinter
{
public void PrintMessage(string msg) => Console.WriteLine($"Old: {msg}");
}
// Interface alvo que seu código espera
public interface IPrinter
{
void Print(string text);
}
// O Adapter faz a ponte
public class PrinterAdapter : IPrinter
{
private OldPrinter _oldPrinter;
public PrinterAdapter(OldPrinter oldPrinter)
{
_oldPrinter = oldPrinter;
}
public void Print(string text)
{
_oldPrinter.PrintMessage(text); // Delega para a interface antiga
}
}Uso:
OldPrinter legacy = new OldPrinter();
IPrinter printer = new PrinterAdapter(legacy);
printer.Print("Hello"); // Old: HelloO código do cliente trabalha com a interface alvo, sem saber que uma classe incompatível está realizando o trabalho real nos bastidores. Este padrão é particularmente valioso ao integrar bibliotecas de terceiros ou código legado.
Experimente você mesmo
using System;
using MediaSystem;
class Program
{
public static void Main(string[] args)
{
// Leia a entrada do nome do arquivo
string filename = Console.ReadLine();
// TODO: Crie uma instância de LegacyAudioPlayer
// TODO: Envolva-a em um MediaPlayerAdapter
// TODO: Use o adaptador por meio da interface IMediaPlayer
// e imprima o resultado da chamada de Play com o nome do arquivo
}
}
Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.
Todas as lições de Programação Orientada a Objetos
1Fundamentos de POO
Arquivos ExternosNamespaces e DiretivasIntrodução a Classes e ObjetosA Palavra-chave 'this'Métodos e ParâmetrosCampos vs PropriedadesConstrutoresInicializadores de ObjetosRecapitulação - Calculadora Simples4Herança
Sintaxe Básica de Herança (:)A Palavra-chave 'base'Palavras-chave Virtual e OverrideClasses SeladasA Classe Base 'object'Recapitulação - Hierarquia de Funcionários7Recursos Avançados
Sobrecarga de OperadoresIndexadores (this[])Sobrescrita de ToString()Métodos de ExtensãoRecapitulação - Lista Personalizada10Padrões de Projeto Parte 1
Introdução aos Padrões de ProjetoSingleton Thread-SafePadrão FactoryPadrão Observer (Eventos)Padrão Strategy2Propriedades e Membros Estáticos
Propriedades AutoimplementadasPropriedades de Apenas Leitura/EscritaCampos e Métodos EstáticosClasses EstáticasMembros com Corpo de Expressão5Polimorfismo e Interfaces
Polimorfismo: Compilação vs ExecuçãoInterface vs Classe AbstrataMúltiplas InterfacesInterfaces ExplícitasUpcasting e DowncastingRecapitulação - Calculadora de Formas8Conceitos Avançados de POO
Composição sobre HerançaGenerics (Classes e Métodos)Delegates e EventosAtributos e ReflectionIDisposable e a Instrução usingFundamentos de Injeção de Dependência11Padrões de Projeto Parte 2
Padrão CommandPadrão AdapterPadrão DecoratorPadrão Template MethodPadrão StatePadrão Composite3Arquitetura de Classes
Dados de Instância vs EstáticosPalavras-chave 'readonly' e 'const'Campos de SuporteRecapitulação - Gerenciador de Conta Bancária6Encapsulamento
Modificadores de AcessoPropriedades para EncapsulamentoImplementação de Ocultação de DadosPadrões de ImutabilidadeRecapitulação - Registros de Estudantes9Argumentos Variáveis
A Palavra-chave 'params'Parâmetros OpcionaisArgumentos NomeadosSobrecarga de Métodos