Menu
Coddy logo textTech

Pattern Adaptateur

Fait partie de la section Programmation Orientée Objet du Journey C++ de Coddy. Leçon 97 sur 104.

Le pattern Adapter permet à des interfaces incompatibles de fonctionner ensemble. Il agit comme un pont entre deux classes, en convertissant l’interface d’une classe en une interface que les clients attendent. Cela est particulièrement utile lors de l’intégration de code existant ou de bibliothèques tierces qui ne correspondent pas à l’interface de votre système.

Imaginez que vous disposez d’une classe existante qui fait ce dont vous avez besoin, mais que son interface ne correspond pas à ce qu’attend votre code. Au lieu de modifier l’un ou l’autre côté, vous créez un adaptateur qui encapsule la classe existante et traduit les appels :

#include <iostream>
#include <string>

// Classe existante avec une interface incompatible
class LegacyPrinter {
public:
    void printMessage(const std::string& msg) {
        std::cout << "Legacy: " << msg << "\n";
    }
};

// Interface cible que le client attend
class Printer {
public:
    virtual void print(const std::string& text) = 0;
    virtual ~Printer() = default;
};

// L'adaptateur encapsule LegacyPrinter et implémente l'interface Printer
class PrinterAdapter : public Printer {
    LegacyPrinter& legacy;
public:
    PrinterAdapter(LegacyPrinter& lp) : legacy(lp) {}
    
    void print(const std::string& text) override {
        legacy.printMessage(text);  // Traduire l'appel
    }
};

int main() {
    LegacyPrinter oldPrinter;
    PrinterAdapter adapter(oldPrinter);
    
    // Le client utilise l'interface attendue
    adapter.print("Hello World");
}

L’adaptateur conserve une référence vers LegacyPrinter et implémente l’interface Printer. Lorsque print() est appelé, il délègue l’opération à printMessage(). Le code client fonctionne avec l’interface Printer sans connaître l’implémentation héritée sous-jacente.

Utilisez Adapter lorsque vous souhaitez utiliser une classe existante, mais que son interface ne correspond pas à vos besoins, ou lorsque vous devez créer une classe réutilisable qui coopère avec des classes ayant des interfaces incompatibles.

challenge icon

Défi

Facile

Construisons un système d’adaptateur de lecteur multimédia qui relie différentes interfaces de bibliothèques audio. Imagine que vous intégriez une bibliothèque audio ancienne dans une application moderne de lecteur multimédia : l’ancienne bibliothèque fonctionne parfaitement, mais son interface ne correspond pas à ce qu’attend votre lecteur. C’est précisément là que le patron Adaptateur révèle toute son utilité.

Vous organiserez votre code sur quatre fichiers :

  • MediaPlayer.h : définissez l’interface cible attendue par votre lecteur multimédia moderne.

    Créez une classe abstraite MediaPlayer avec ces méthodes virtuelles pures :

    • play(const std::string& filename) : lit le fichier indiqué
    • stop() : arrête la lecture
    • setVolume(int level) : définit le volume (0-100)

    Ajoutez un destructeur virtuel.

  • LegacyAudioLib.h : créez la bibliothèque audio « ancienne » existante avec une interface incompatible.

    Votre classe LegacyAudioLib représente une ancienne bibliothèque qui fonctionne différemment de ce qu’attend votre lecteur. Elle doit disposer des éléments suivants :

    • loadAudio(const std::string& path) : affiche Legacy: Loading [path]
    • startPlayback() : affiche Legacy: Playback started
    • stopPlayback() : affiche Legacy: Playback stopped
    • adjustVolume(double percentage) : accepte une valeur décimale (0.0 à 1.0) et affiche Legacy: Volume set to [percentage] (en affichant la valeur décimale)
  • AudioAdapter.h : construisez l’adaptateur qui relie les deux interfaces.

    Votre classe AudioAdapter doit hériter de MediaPlayer et conserver une référence vers un objet LegacyAudioLib. L’adaptateur traduit les appels de l’interface moderne vers l’interface ancienne :

    • play() doit appeler à la fois loadAudio() et startPlayback() sur la bibliothèque ancienne
    • stop() doit déléguer à stopPlayback()
    • setVolume() reçoit un entier (0-100), mais la bibliothèque ancienne attend une valeur décimale (0.0-1.0) ; vous devrez donc effectuer la conversion
  • main.cpp : démontrez le fonctionnement de l’adaptateur.

    Lisez deux entrées :

    1. Un nom de fichier à lire (chaîne de caractères)
    2. Un niveau de volume (entier, 0-100)

    Créez une instance de LegacyAudioLib et un AudioAdapter qui l’enveloppe. Utilisez ensuite l’adaptateur via l’interface MediaPlayer (stockez-le dans un pointeur MediaPlayer) pour :

    1. Définir le volume sur le niveau saisi
    2. Lire le nom de fichier saisi
    3. Arrêter la lecture

Par exemple, avec les entrées song.mp3 et 75 :

Legacy: Volume set to 0.75
Legacy: Loading song.mp3
Legacy: Playback started
Legacy: Playback stopped

Avec les entrées podcast.wav et 50 :

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

Remarquez que le code client fonctionne entièrement avec l’interface MediaPlayer. Il ne sait pas qu’une bibliothèque ancienne effectue réellement le travail en arrière-plan. L’adaptateur gère toute la traduction, notamment la conversion du volume entier en valeur décimale. C’est l’essence du patron Adaptateur : faire fonctionner ensemble des interfaces incompatibles sans modifier l’un ou l’autre côté.

Essayez vous-même

#include <iostream>
#include <string>
#include "MediaPlayer.h"
#include "LegacyAudioLib.h"
#include "AudioAdapter.h"

using namespace std;

int main() {
    // Lire les entrées
    string filename;
    int volumeLevel;
    cin >> filename;
    cin >> volumeLevel;
    
    // TODO: Créer une instance de LegacyAudioLib
    
    // TODO: Créer un AudioAdapter qui encapsule la bibliothèque legacy
    
    // TODO: Stocker l'adaptateur en tant que pointeur MediaPlayer
    
    // TODO: Utiliser l'interface MediaPlayer pour :
    // 1. Régler le volume au niveau d'entrée
    // 2. Lire le nom de fichier d'entrée
    // 3. Arrêter la lecture
    
    return 0;
}
quiz iconTestez-vous

Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.

Toutes les leçons de Programmation Orientée Objet

Entraînez-vous par vous-même : Compilateur C++ en ligne