Menu
Coddy logo textTech

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.

challenge icon

Desafio

Fácil

Vamos 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 MediaPlayer com estes métodos virtuais puros:

    • play(const std::string& filename): reproduz o arquivo especificado
    • stop(): interrompe a reprodução
    • setVolume(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 LegacyAudioLib representa uma biblioteca antiga que funciona de maneira diferente do que o seu reprodutor espera. Ela deve ter:

    • loadAudio(const std::string& path): imprime Legacy: Loading [path]
    • startPlayback(): imprime Legacy: Playback started
    • stopPlayback(): imprime Legacy: Playback stopped
    • adjustVolume(double percentage): recebe um valor decimal (0.0 a 1.0) e imprime Legacy: Volume set to [percentage] (mostrando o valor decimal)
  • AudioAdapter.h: crie o adaptador que conecta as duas interfaces.

    Sua classe AudioAdapter deve herdar de MediaPlayer e manter uma referência a um objeto LegacyAudioLib. O adaptador traduz as chamadas da interface moderna para a interface legada:

    • play() deve chamar tanto loadAudio() quanto startPlayback() na biblioteca legada
    • stop() deve delegar para stopPlayback()
    • 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:

    1. Um nome de arquivo para reproduzir (string)
    2. Um nível de volume (inteiro, 0-100)

    Crie uma instância de LegacyAudioLib e um AudioAdapter que a envolva. Em seguida, use o adaptador por meio da interface MediaPlayer (armazene-o como um ponteiro de MediaPlayer) para:

    1. Definir o volume para o nível informado
    2. Reproduzir o nome de arquivo informado
    3. 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 stopped

Com as entradas podcast.wav e 50:

Legacy: Volume set to 0.5
Legacy: Loading podcast.wav
Legacy: Playback started
Legacy: Playback stopped

Observe 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;
}
quiz iconTeste seus conhecimentos

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

Pratique por conta própria: Compilador de C++ online