Padrão Adapter
Parte da seção Programação Orientada a Objetos do Journey de C++ da Coddy. Lição 97 de 104.
O padrão Adapter permite que interfaces incompatíveis trabalhem juntas. Ele atua como uma ponte entre duas classes, convertendo a interface de uma classe em uma interface que os clientes esperam. Isso é particularmente útil ao integrar código legado ou bibliotecas de terceiros que não correspondem à interface do seu sistema.
Imagine que você tenha uma classe existente que faz o que você precisa, mas cuja interface não corresponde ao que seu código espera. Em vez de modificar qualquer um dos lados, você cria um adaptador que encapsula a classe existente e traduz as chamadas:
#include <iostream>
#include <string>
// Classe existente com interface incompatível
class LegacyPrinter {
public:
void printMessage(const std::string& msg) {
std::cout << "Legacy: " << msg << "\n";
}
};
// Interface alvo que o cliente espera
class Printer {
public:
virtual void print(const std::string& text) = 0;
virtual ~Printer() = default;
};
// Adapter envolve LegacyPrinter e implementa a interface Printer
class PrinterAdapter : public Printer {
LegacyPrinter& legacy;
public:
PrinterAdapter(LegacyPrinter& lp) : legacy(lp) {}
void print(const std::string& text) override {
legacy.printMessage(text); // Traduz a chamada
}
};
int main() {
LegacyPrinter oldPrinter;
PrinterAdapter adapter(oldPrinter);
// Cliente usa a interface esperada
adapter.print("Hello World");
}O adaptador mantém uma referência a LegacyPrinter e implementa a interface Printer. Quando print() é chamado, ele delega para printMessage(). O código do cliente trabalha com a interface Printer sem conhecer a implementação legada subjacente.
Use o Adapter quando quiser usar uma classe existente, mas a interface dela não corresponder ao que você precisa, ou quando precisar criar uma classe reutilizável que coopere com classes que tenham interfaces incompatíveis.
Desafio
FácilVamos criar um sistema de Adaptador de Reprodutor de Mídia que conecta diferentes interfaces de bibliotecas de áudio. Imagine que você esteja integrando uma biblioteca de áudio legada a um aplicativo moderno de reprodução de mídia: a biblioteca antiga funciona perfeitamente, mas sua interface não corresponde ao que o seu reprodutor espera. É exatamente nesse ponto que o padrão Adapter se destaca.
Você organizará seu código em quatro arquivos:
MediaPlayer.h: defina a interface-alvo que o seu reprodutor de mídia moderno espera.Crie uma classe abstrata
MediaPlayercom estes métodos virtuais puros:play(const std::string& filename): reproduz o arquivo especificadostop(): interrompe a reproduçãosetVolume(int level): define o volume (0-100)
Inclua um destrutor virtual.
LegacyAudioLib.h: crie a biblioteca de áudio "legada" existente com uma interface incompatível.Sua classe
LegacyAudioLibrepresenta uma biblioteca antiga que funciona de maneira diferente do que o seu reprodutor espera. Ela deve ter:loadAudio(const std::string& path): imprimeLegacy: Loading [path]startPlayback(): imprimeLegacy: Playback startedstopPlayback(): imprimeLegacy: Playback stoppedadjustVolume(double percentage): recebe um valor decimal (0.0 a 1.0) e imprimeLegacy: Volume set to [percentage](mostrando o valor decimal)
AudioAdapter.h: crie o adaptador que conecta as duas interfaces.Sua classe
AudioAdapterdeve herdar deMediaPlayere manter uma referência a um objetoLegacyAudioLib. O adaptador traduz as chamadas da interface moderna para a interface legada:play()deve chamar tantoloadAudio()quantostartPlayback()na biblioteca legadastop()deve delegar parastopPlayback()setVolume()recebe um inteiro (0-100), mas a biblioteca legada espera um valor decimal (0.0-1.0), portanto você precisará convertê-lo
main.cpp: demonstre o adaptador em ação.Leia duas entradas:
- Um nome de arquivo para reproduzir (string)
- Um nível de volume (inteiro, 0-100)
Crie uma instância de
LegacyAudioLibe umAudioAdapterque a envolva. Em seguida, use o adaptador por meio da interfaceMediaPlayer(armazene-o como um ponteiro deMediaPlayer) para:- Definir o volume para o nível informado
- Reproduzir o nome de arquivo informado
- Interromper a reprodução
Por exemplo, com as entradas song.mp3 e 75:
Legacy: Volume set to 0.75
Legacy: Loading song.mp3
Legacy: Playback started
Legacy: Playback stoppedCom as entradas podcast.wav e 50:
Legacy: Volume set to 0.5
Legacy: Loading podcast.wav
Legacy: Playback started
Legacy: Playback stoppedObserve como o código cliente trabalha inteiramente com a interface MediaPlayer. Ele não faz ideia de que uma biblioteca legada está realizando o trabalho efetivamente por baixo dos panos. O adaptador cuida de toda a tradução, incluindo a conversão do volume inteiro para um valor decimal. Essa é a essência do padrão Adapter: fazer com que interfaces incompatíveis trabalhem juntas sem modificar nenhum dos lados.
Experimente você mesmo
#include <iostream>
#include <string>
#include "MediaPlayer.h"
#include "LegacyAudioLib.h"
#include "AudioAdapter.h"
using namespace std;
int main() {
// Ler entradas
string filename;
int volumeLevel;
cin >> filename;
cin >> volumeLevel;
// TODO: Criar uma instância de LegacyAudioLib
// TODO: Criar um AudioAdapter que encapsula a biblioteca legada
// TODO: Armazenar o adapter como um ponteiro MediaPlayer
// TODO: Usar a interface MediaPlayer para:
// 1. Definir o volume para o nível de entrada
// 2. Reproduzir o nome de arquivo de entrada
// 3. Parar a reprodução
return 0;
}
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 externosBuild e compilação em C++Arquivos de cabeçalho e arquivos-fonteNamespaces e escopoIntrodução à POO em C++Classes vs. objetosO ponteiro 'this'Métodos (funções membro)Atributos (membros de dados)Noções básicas de construtores e destruidoresRevisão - calculadora simples4Propriedades de Classe
Membros de Instância vs. EstáticosGetters e SettersFunções Membro ConstPalavra-chave MutableMétodos e Variáveis EstáticosFunções e Classes FriendRevisão - Gerenciador de Contas Bancárias7Herança
Herança básicaNíveis de acesso da herançaOrdem de chamada de construtor e destrutorSobrescrita de métodosFunções virtuais e VTableHerança múltiplaHerança virtualRevisão - Hierarquia de funcionários10Visão geral da STL
Visão geral e filosofia da STLContêineres da STLIteradoresAlgoritmos da STLFunctors e expressões lambdaRecapitulação - Frequência de palavras13Padrões de Projeto — Parte 1
Introdução aos Padrões de ProjetoPadrão SingletonFactory e Abstract FactoryPadrão BuilderPadrão ObserverPadrão Strategy2Gerenciamento de Memória
Memória Stack vs. HeapPonteiros e ReferênciasMemória Dinâmica (new/delete)Ponteiros Inteligentes em C++RAII em C++Revisão - Gerenciador de Array Dinâmico5Encapsulamento
Especificadores de acesso em C++Especificadores de acesso em profundidadeOcultação de informaçõesStruct vs ClasseClasses aninhadas e internasRecapitulação - Sistema de registros de alunos8Polimorfismo
Polimorfismo em compilação vs. execuçãoSobrecarga de funçõesFunções virtuais revisadasFunções virtuais purasClasses abstratasDesign de interfaces em C++Conversão dinâmica e RTTIRecapitulação — Calculadora de formas11Conceitos Avançados de OOP
Composição vs. HerançaMixins via CRTPIdioma PimplApagamento de TiposClasses Enum e Tipagem ForteTratamento de Exceções em OOPHierarquias de Exceções Personalizadas14Padrões de Projeto — Parte 2
Padrão CommandPadrão AdapterPadrão DecoratorPadrão Template MethodPadrão StatePadrão CompositeRAII como Padrão3Construtores e Destrutores
Construtor PadrãoConstrutor ParametrizadoConstrutor de CópiaConstrutor de MovimentoListas de Inicialização do ConstrutorConstrutores DelegantesAprofundamento em DestrutoresRegra dos Três / Cinco / ZeroRevisão - Classe String6Sobrecarga de Operadores
Introdução à Sobrecarga de OperadoresSobrecarga de Operadores AritméticosSobrecarga de Operadores de ComparaçãoOperadores de FluxoSobrecarga do Operador de AtribuiçãoSobrecarga dos Operadores [] e ()Operadores de Conversão de TipoRecapitulação - Classe Matrix9Templates
Templates de FunçõesTemplates de ClassesEspecialização de TemplatesTemplates VariádicosNoções Básicas de SFINAE e Traits de TiposRecapitulação - Contêiner GenéricoPratique por conta própria: Compilador de C++ online