Menu
Coddy logo textTech

Adaptermuster

Teil des Abschnitts Objektorientierte Programmierung der C++-Journey von Coddy. Lektion 97 von 104.

Das Adapter-Muster ermöglicht es, dass inkompatible Schnittstellen zusammenarbeiten. Es fungiert als Brücke zwischen zwei Klassen und wandelt die Schnittstelle einer Klasse in eine Schnittstelle um, die Clients erwarten. Dies ist besonders nützlich bei der Integration von Legacy-Code oder Drittanbieterbibliotheken, deren Schnittstelle nicht mit der Ihres Systems übereinstimmt.

Stell dir vor, du hast eine bestehende Klasse, die genau das tut, was du benötigst, aber ihre Schnittstelle nicht dem entspricht, was dein Code erwartet. Anstatt eine der beiden Seiten zu ändern, erstellst du einen Adapter, der die bestehende Klasse umschließt und Aufrufe übersetzt:

#include <iostream>
#include <string>

// Bestehende Klasse mit inkompatibler Schnittstelle
class LegacyPrinter {
public:
    void printMessage(const std::string& msg) {
        std::cout << "Legacy: " << msg << "\n";
    }
};

// Zielschnittstelle, die der Client erwartet
class Printer {
public:
    virtual void print(const std::string& text) = 0;
    virtual ~Printer() = default;
};

// Adapter umhüllt LegacyPrinter und implementiert die Printer-Schnittstelle
class PrinterAdapter : public Printer {
    LegacyPrinter& legacy;
public:
    PrinterAdapter(LegacyPrinter& lp) : legacy(lp) {}
    
    void print(const std::string& text) override {
        legacy.printMessage(text);  // Übersetze den Aufruf
    }
};

int main() {
    LegacyPrinter oldPrinter;
    PrinterAdapter adapter(oldPrinter);
    
    // Client verwendet die erwartete Schnittstelle
    adapter.print("Hello World");
}

Der Adapter enthält eine Referenz auf LegacyPrinter und implementiert die Printer-Schnittstelle. Wenn print() aufgerufen wird, delegiert er an printMessage(). Der Clientcode arbeitet mit der Printer-Schnittstelle, ohne die darunterliegende Legacy-Implementierung zu kennen.

Verwende den Adapter, wenn du eine vorhandene Klasse verwenden möchtest, ihre Schnittstelle jedoch nicht dem entspricht, was du benötigst, oder wenn du eine wiederverwendbare Klasse erstellen musst, die mit Klassen mit inkompatiblen Schnittstellen zusammenarbeitet.

challenge icon

Aufgabe

Einfach

Lass uns ein Media Player Adapter-System entwickeln, das verschiedene Audio-Bibliotheksschnittstellen verbindet. Stell dir vor, du integrierst eine veraltete Audiobibliothek in eine moderne Media-Player-Anwendung: Die alte Bibliothek funktioniert perfekt, aber ihre Schnittstelle entspricht nicht den Erwartungen deines Players. Genau hier spielt das Adapter-Muster seine Stärke aus.

Du wirst deinen Code auf vier Dateien verteilen:

  • MediaPlayer.h: Definiere die Zielschnittstelle, die dein moderner Media Player erwartet.

    Erstelle eine abstrakte MediaPlayer-Klasse mit diesen rein virtuellen Methoden:

    • play(const std::string& filename): spielt die angegebene Datei ab
    • stop(): beendet die Wiedergabe
    • setVolume(int level): legt die Lautstärke fest (0–100)

    Füge einen virtuellen Destruktor hinzu.

  • LegacyAudioLib.h: Erstelle die bestehende „Legacy“-Audiobibliothek mit einer inkompatiblen Schnittstelle.

    Deine LegacyAudioLib-Klasse repräsentiert eine alte Bibliothek, die anders funktioniert als von deinem Player erwartet. Sie sollte Folgendes enthalten:

    • loadAudio(const std::string& path): gibt Legacy: Loading [path] aus
    • startPlayback(): gibt Legacy: Playback started aus
    • stopPlayback(): gibt Legacy: Playback stopped aus
    • adjustVolume(double percentage): erwartet eine Dezimalzahl (0.0 bis 1.0) und gibt Legacy: Volume set to [percentage] aus (zeige den Dezimalwert an)
  • AudioAdapter.h: Erstelle den Adapter, der die beiden Schnittstellen verbindet.

    Deine AudioAdapter-Klasse sollte von MediaPlayer erben und eine Referenz auf ein LegacyAudioLib-Objekt halten. Der Adapter übersetzt Aufrufe der modernen Schnittstelle in Aufrufe der Legacy-Schnittstelle:

    • play() sollte sowohl loadAudio() als auch startPlayback() für die Legacy-Bibliothek aufrufen
    • stop() sollte an stopPlayback() delegieren
    • setVolume() erhält eine Ganzzahl (0–100), aber die Legacy-Bibliothek erwartet eine Dezimalzahl (0.0–1.0), daher musst du sie umwandeln
  • main.cpp: Zeige den Adapter in Aktion.

    Lies zwei Eingaben ein:

    1. Einen abzuspielenden Dateinamen (String)
    2. Einen Lautstärkepegel (Ganzzahl, 0–100)

    Erstelle eine LegacyAudioLib-Instanz und einen AudioAdapter, der sie umschließt. Verwende anschließend den Adapter über die MediaPlayer-Schnittstelle (speichere ihn als MediaPlayer-Zeiger), um:

    1. Die Lautstärke auf den Eingabepegel zu setzen
    2. Den eingegebenen Dateinamen abzuspielen
    3. Die Wiedergabe zu beenden

Beispielsweise mit den Eingaben song.mp3 und 75:

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

Mit den Eingaben podcast.wav und 50:

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

Beachte, dass der Client-Code vollständig mit der MediaPlayer-Schnittstelle arbeitet. Er weiß nicht, dass im Hintergrund tatsächlich eine Legacy-Bibliothek die Arbeit erledigt. Der Adapter übernimmt die gesamte Übersetzung, einschließlich der Umwandlung der ganzzahligen Lautstärke in eine Dezimalzahl. Das ist das Wesentliche des Adapter-Musters: inkompatible Schnittstellen zusammenarbeiten zu lassen, ohne eine der beiden Seiten zu verändern.

Probier es selbst

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

using namespace std;

int main() {
    // Eingaben lesen
    string filename;
    int volumeLevel;
    cin >> filename;
    cin >> volumeLevel;
    
    // TODO: Erstelle eine LegacyAudioLib-Instanz
    
    // TODO: Erstelle einen AudioAdapter, der die Legacy-Bibliothek umhüllt
    
    // TODO: Speichere den Adapter als MediaPlayer-Zeiger
    
    // TODO: Verwende die MediaPlayer-Schnittstelle, um:
    // 1. Setze die Lautstärke auf den Eingabepegel
    // 2. Spiele den Eingabe-Dateinamen ab
    // 3. Stoppe die Wiedergabe
    
    return 0;
}
quiz iconTeste dich selbst

Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.

Alle Lektionen in Objektorientierte Programmierung

Übe selbstständig: Online-C++-Compiler