Pattern Adapter
Fa parte della sezione Programmazione orientata agli oggetti del percorso C++ di Coddy. Lezione 97 di 104.
Il pattern Adapter consente a interfacce incompatibili di lavorare insieme. Funge da ponte tra due classi, convertendo l'interfaccia di una classe in un'interfaccia prevista dai client. È particolarmente utile quando si integra codice legacy o librerie di terze parti che non corrispondono all'interfaccia del sistema.
Immagina di avere una classe esistente che fa ciò di cui hai bisogno, ma la sua interfaccia non corrisponde a ciò che il tuo codice si aspetta. Invece di modificare uno dei due lati, crei un adattatore che avvolge la classe esistente e traduce le chiamate:
#include <iostream>
#include <string>
// Classe esistente con un'interfaccia incompatibile
class LegacyPrinter {
public:
void printMessage(const std::string& msg) {
std::cout << "Legacy: " << msg << "\n";
}
};
// Interfaccia di destinazione prevista dal client
class Printer {
public:
virtual void print(const std::string& text) = 0;
virtual ~Printer() = default;
};
// L'adapter incapsula LegacyPrinter e implementa l'interfaccia Printer
class PrinterAdapter : public Printer {
LegacyPrinter& legacy;
public:
PrinterAdapter(LegacyPrinter& lp) : legacy(lp) {}
void print(const std::string& text) override {
legacy.printMessage(text); // Adatta la chiamata
}
};
int main() {
LegacyPrinter oldPrinter;
PrinterAdapter adapter(oldPrinter);
// Il client usa l'interfaccia prevista
adapter.print("Hello World");
}L’adattatore mantiene un riferimento a LegacyPrinter e implementa l’interfaccia Printer. Quando viene chiamato print(), delega a printMessage(). Il codice client lavora con l’interfaccia Printer senza conoscere l’implementazione legacy sottostante.
Usa Adapter quando vuoi usare una classe esistente ma la sua interfaccia non corrisponde a ciò di cui hai bisogno, oppure quando devi creare una classe riutilizzabile che collabori con classi dotate di interfacce incompatibili.
Sfida
FacileCostruiamo un sistema di adattatore per lettore multimediale che collega interfacce diverse di librerie audio. Immagina di integrare una libreria audio legacy in un'applicazione moderna di riproduzione multimediale: la vecchia libreria funziona perfettamente, ma la sua interfaccia non corrisponde a ciò che il tuo lettore si aspetta. È proprio qui che il pattern Adapter dà il meglio di sé.
Organizzerai il codice in quattro file:
MediaPlayer.h: definisci l'interfaccia di destinazione che il tuo moderno lettore multimediale si aspetta.Crea una classe astratta
MediaPlayercon questi metodi virtuali puri:play(const std::string& filename): riproduce il file specificatostop(): interrompe la riproduzionesetVolume(int level): imposta il volume (0-100)
Includi un distruttore virtuale.
LegacyAudioLib.h: crea la libreria audio "legacy" esistente, con un'interfaccia incompatibile.La tua classe
LegacyAudioLibrappresenta una vecchia libreria che funziona in modo diverso da quello che il tuo lettore si aspetta. Dovrebbe avere:loadAudio(const std::string& path): stampaLegacy: Loading [path]startPlayback(): stampaLegacy: Playback startedstopPlayback(): stampaLegacy: Playback stoppedadjustVolume(double percentage): accetta un decimale (da 0.0 a 1.0) e stampaLegacy: Volume set to [percentage](mostra il valore decimale)
AudioAdapter.h: crea l'adattatore che collega le due interfacce.La tua classe
AudioAdapterdovrebbe ereditare daMediaPlayere mantenere un riferimento a un oggettoLegacyAudioLib. L'adattatore traduce le chiamate dall'interfaccia moderna a quella legacy:play()dovrebbe chiamare sialoadAudio()siastartPlayback()sulla libreria legacystop()dovrebbe delegare astopPlayback()setVolume()riceve un intero (0-100), ma la libreria legacy si aspetta un decimale (0.0-1.0), quindi dovrai convertirlo
main.cpp: mostra l'adattatore in azione.Leggi due input:
- Un nome di file da riprodurre (stringa)
- Un livello del volume (intero, 0-100)
Crea un'istanza di
LegacyAudioLibe unAudioAdapterche la avvolga. Poi usa l'adattatore tramite l'interfacciaMediaPlayer(memorizzalo come puntatore aMediaPlayer) per:- Impostare il volume al livello specificato dall'input
- Riprodurre il nome di file specificato dall'input
- Interrompere la riproduzione
Ad esempio, con gli input song.mp3 e 75:
Legacy: Volume set to 0.75
Legacy: Loading song.mp3
Legacy: Playback started
Legacy: Playback stoppedCon gli input podcast.wav e 50:
Legacy: Volume set to 0.5
Legacy: Loading podcast.wav
Legacy: Playback started
Legacy: Playback stoppedNota che il codice client usa interamente l'interfaccia MediaPlayer. Non sa che sotto, a svolgere il lavoro vero e proprio, c'è una libreria legacy. L'adattatore gestisce tutta la traduzione, compresa la conversione del volume da intero a decimale. Questa è l'essenza del pattern Adapter: far funzionare insieme interfacce incompatibili senza modificare nessuna delle due.
Provalo tu
#include <iostream>
#include <string>
#include "MediaPlayer.h"
#include "LegacyAudioLib.h"
#include "AudioAdapter.h"
using namespace std;
int main() {
// Leggi gli input
string filename;
int volumeLevel;
cin >> filename;
cin >> volumeLevel;
// TODO: Crea un'istanza di LegacyAudioLib
// TODO: Crea un AudioAdapter che incapsuli la libreria legacy
// TODO: Memorizza l'adapter come puntatore a MediaPlayer
// TODO: Usa l'interfaccia MediaPlayer per:
// 1. Impostare il volume al livello specificato nell'input
// 2. Riprodurre il file specificato dal nome nell'input
// 3. Interrompere la riproduzione
return 0;
}
Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.
Tutte le lezioni di Programmazione orientata agli oggetti
1Fondamenti della programmazione orientata agli oggetti
File esterniBuild e compilazione in C++File header e file sorgenteNamespace e ambitoIntroduzione alla programmazione orientata agli oggetti in C++Classi e oggetti a confrontoIl puntatore 'this'Metodi (funzioni membro)Attributi (membri dati)Fondamenti di costruttori e distruttoriRiepilogo - Calcolatrice semplice4Proprietà delle classi
Membri di istanza e staticiGetter e setterFunzioni membro constParola chiave mutableMetodi e variabili staticiFunzioni e classi friendRiepilogo - Gestore di conti bancari7Ereditarietà
Ereditarietà di baseLivelli di accesso nell’ereditarietàOrdine di chiamata di costruttori e distruttoriRidefinizione dei metodiFunzioni virtuali e VTableEreditarietà multiplaEreditarietà virtualeRiepilogo - Gerarchia dei dipendenti2Gestione della memoria
Memoria Stack vs HeapPuntatori e riferimentiMemoria dinamica (new/delete)Puntatori intelligenti in C++RAII in C++Riepilogo - Gestore di array dinamico5Incapsulamento
Specificatori di accesso in C++Specificatori di accesso in dettaglioOccultamento delle informazioniStruct vs classClassi annidate e interneRiepilogo - Sistema di registrazione degli studenti8Polimorfismo
Polimorfismo a compile time e a runtimeOverload delle funzioniFunzioni virtuali: ripassoFunzioni virtuali pureClassi astratteProgettazione delle interfacce in C++Dynamic casting e RTTIRipasso: calcolatrice di forme11Concetti avanzati di OOP
Composizione vs ereditarietàMixin tramite CRTPIdiom PimplType ErasureEnum class e tipizzazione forteGestione delle eccezioni in OOPGerarchie personalizzate di eccezioni14Pattern di progettazione - Parte 2
Pattern CommandPattern AdapterPattern DecoratorPattern Template MethodPattern StatePattern CompositeRAII come pattern3Costruttori e distruttori
Costruttore predefinitoCostruttore con parametriCostruttore di copiaCostruttore di spostamentoListe di inizializzazione dei costruttoriCostruttori delegantiApprofondimento sui distruttoriRegola del Tre / Cinque / ZeroRipasso - classe String6Sovraccarico degli operatori
Introduzione al sovraccarico degli operatoriSovraccarico degli operatori aritmeticiSovraccarico degli operatori di confrontoOperatori di flussoSovraccarico dell'operatore di assegnazioneSovraccarico degli operatori [] e ()Operatori di conversione di tipoRipasso - Classe Matrix9Template
Template di funzioneTemplate di classeSpecializzazione dei templateTemplate variadiciBasi di SFINAE e dei trait di tipoRiepilogo - Contenitore genericoEsercitati da solo: Compilatore C++ online