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.
Défi
FacileConstruisons 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
MediaPlayeravec ces méthodes virtuelles pures :play(const std::string& filename): lit le fichier indiquéstop(): arrête la lecturesetVolume(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
LegacyAudioLibrepré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): afficheLegacy: Loading [path]startPlayback(): afficheLegacy: Playback startedstopPlayback(): afficheLegacy: Playback stoppedadjustVolume(double percentage): accepte une valeur décimale (0.0 à 1.0) et afficheLegacy: Volume set to [percentage](en affichant la valeur décimale)
AudioAdapter.h: construisez l’adaptateur qui relie les deux interfaces.Votre classe
AudioAdapterdoit hériter deMediaPlayeret conserver une référence vers un objetLegacyAudioLib. L’adaptateur traduit les appels de l’interface moderne vers l’interface ancienne :play()doit appeler à la foisloadAudio()etstartPlayback()sur la bibliothèque anciennestop()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 :
- Un nom de fichier à lire (chaîne de caractères)
- Un niveau de volume (entier, 0-100)
Créez une instance de
LegacyAudioLibet unAudioAdapterqui l’enveloppe. Utilisez ensuite l’adaptateur via l’interfaceMediaPlayer(stockez-le dans un pointeurMediaPlayer) pour :- Définir le volume sur le niveau saisi
- Lire le nom de fichier saisi
- 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 stoppedAvec les entrées podcast.wav et 50 :
Legacy: Volume set to 0.5
Legacy: Loading podcast.wav
Legacy: Playback started
Legacy: Playback stoppedRemarquez 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;
}
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
1Fondamentaux de la POO
Fichiers externesGénération et compilation en C++Fichiers d’en-tête et fichiers sourceEspaces de noms et portéeIntroduction à la POO en C++Classes et objetsLe pointeur « this »Méthodes (fonctions membres)Attributs (membres de données)Bases des constructeurs et destructeursRécapitulatif - Calculatrice simple4Propriétés de classe
Membres d’instance et statiquesAccesseurs et mutateursFonctions membres constMot-clé mutableMéthodes et variables statiquesFonctions et classes amiesRécapitulatif - Gestionnaire de compte bancaire7Héritage
Héritage de baseNiveaux d’accès de l’héritageOrdre d’appel des constructeurs et destructeursRedéfinition des méthodesFonctions virtuelles et VTableHéritage multipleHéritage virtuelRécapitulatif : hiérarchie des employés10Vue d’ensemble de la STL
Vue d’ensemble et philosophie de la STLConteneurs de la STLItérateursAlgorithmes de la STLFoncteurs et expressions lambdaRécapitulatif - fréquence des mots13Modèles de conception, partie 1
Introduction aux modèles de conceptionModèle SingletonFabrique et fabrique abstraiteModèle BuilderModèle ObserverModèle Strategy2Gestion de la mémoire
Mémoire de pile ou de tasPointeurs et référencesMémoire dynamique (new/delete)Pointeurs intelligents en C++RAII en C++Récapitulatif - Gestionnaire de tableaux dynamiques5Encapsulation
Spécificateurs d’accès en C++Spécificateurs d’accès en profondeurMasquage de l’informationStruct vs classeClasses imbriquées et internesRécapitulatif - Système de gestion des dossiers étudiants8Polymorphisme
Polymorphisme à la compilation vs à l’exécutionSurcharge de fonctionsRetour sur les fonctions virtuellesFonctions virtuelles puresClasses abstraitesConception d’interfaces en C++Conversion dynamique et RTTIRécapitulatif - Calculateur de formes11Concepts avancés de la POO
Composition ou héritageMixins via CRTPIdiom PimplEffacement de typeClasses enum et typage fortGestion des exceptions en POOHiérarchies d’exceptions personnalisées14Design Patterns – Partie 2
Pattern CommandePattern AdaptateurPattern DécorateurPattern Méthode ModèlePattern ÉtatPattern CompositeRAII comme Pattern3Constructeurs et destructeurs
Constructeur par défautConstructeur paramétréConstructeur de copieConstructeur de déplacementListes d’initialisation des constructeursConstructeurs déléguésApprofondissement des destructeursRègle des trois / cinq / zéroRécapitulatif - Classe String6Surcharge des opérateurs
Introduction à la surcharge des opérateursSurcharge des opérateurs arithmétiquesSurcharge des opérateurs de comparaisonOpérateurs de fluxSurcharge de l’opérateur d’affectationSurcharge des opérateurs [] et ()Opérateurs de conversion de typeRécapitulatif – Classe Matrix9Templates
Templates de fonctionsTemplates de classesSpécialisation des templatesTemplates variadiquesBases de SFINAE et des traits de typesRécapitulatif - Conteneur génériqueEntraînez-vous par vous-même : Compilateur C++ en ligne