Adaptermuster
Teil des Abschnitts Objektorientierte Programmierung der C#-Journey von Coddy. Lektion 57 von 70.
Das Adapter-Muster ist ein strukturelles Muster, das inkompatiblen Schnittstellen ermöglicht, zusammenzuarbeiten. Es fungiert als Brücke zwischen zwei Klassen und wandelt die Schnittstelle einer Klasse in eine Schnittstelle um, die Clients erwarten. Stell es dir wie einen Netzadapter vor, mit dem du ein europäisches Gerät an eine amerikanische Steckdose anschließen kannst.
Dieses Muster ist besonders nützlich, wenn du eine vorhandene Klasse verwenden möchtest, deren Schnittstelle jedoch nicht dem entspricht, was dein Code benötigt. Anstatt die ursprüngliche Klasse zu ändern (auf die du möglicherweise keinen Einfluss hast), erstellst du einen Adapter, der sie kapselt:
// Bestehende Klasse mit inkompatibler Schnittstelle
public class OldPrinter
{
public void PrintMessage(string msg) => Console.WriteLine($"Old: {msg}");
}
// Schnittstelle, die Ihr Code erwartet
public interface IPrinter
{
void Print(string text);
}
// Adapter überbrückt die Lücke
public class PrinterAdapter : IPrinter
{
private OldPrinter _oldPrinter;
public PrinterAdapter(OldPrinter oldPrinter)
{
_oldPrinter = oldPrinter;
}
public void Print(string text)
{
_oldPrinter.PrintMessage(text); // Delegiert an die alte Schnittstelle
}
}Jetzt kann Ihr Code über die erwartete Schnittstelle mit dem alten Drucker arbeiten:
OldPrinter legacy = new OldPrinter();
IPrinter printer = new PrinterAdapter(legacy);
printer.Print("Hello"); // Alt: HelloDas Adapter-Muster ist bei der Integration von Bibliotheken von Drittanbietern oder Legacy-Code in deine Anwendung von unschätzbarem Wert. Der Client-Code arbeitet mit der Schnittstelle IPrinter, ohne zu wissen, dass eine alte, inkompatible Klasse im Hintergrund tatsächlich die Arbeit erledigt.
Aufgabe
EinfachErstellen wir ein Mediaplayer-System mithilfe des Adapter-Patterns. Du hast eine vorhandene Audiobibliothek, die MP3-Dateien abspielt, aber deine Anwendung benötigt eine einheitliche Schnittstelle, die verschiedene Medientypen verarbeiten kann. Du erstellst einen Adapter, der diese Lücke überbrückt, sodass dein moderner Mediaplayer nahtlos mit der älteren Audiobibliothek zusammenarbeiten kann.
Du organisierst deinen Code über drei Dateien:
LegacyAudio.cs: Dies stellt eine vorhandene Bibliothek dar, die du nicht ändern kannst. Erstelle eineLegacyAudioPlayer-Klasse im NamespaceMediaSystemmit einer MethodePlayMp3(string filename), die den String"Playing MP3: {filename}"zurückgibt. Dies ist dein „Adaptee“ – die Klasse mit der inkompatiblen Schnittstelle.MediaAdapter.cs: Erstelle die Schnittstelle und den Adapter im selben Namespace. Definiere eineIMediaPlayer-Schnittstelle mit einer MethodePlay(string filename), die einen String zurückgibt. Erstelle anschließend eineMediaPlayerAdapter-Klasse, dieIMediaPlayerimplementiert. Dein Adapter sollte eineLegacyAudioPlayer-Instanz umschließen, die über den Konstruktor übergeben wird, und den Aufruf vonPlayin einen Aufruf der MethodePlayMp3des älteren Players übersetzen.Program.cs: Führe alles zusammen, indem du den älteren Player erstellst, ihn in deinen Adapter einbindest und den Adapter über dieIMediaPlayer-Schnittstelle verwendest. Dies zeigt, wie Client-Code mit der modernen Schnittstelle arbeiten kann, während der Adapter die Kommunikation mit dem älteren System im Hintergrund übernimmt.
Du erhältst eine Eingabe:
- Der abzuspielende Dateiname (z. B.
song.mp3)
Erstelle einen LegacyAudioPlayer, binde ihn in einen MediaPlayerAdapter ein, rufe anschließend Play über die IMediaPlayer-Schnittstelle auf dem Adapter auf und gib das Ergebnis aus.
Wenn die Eingabe beispielsweise favorite_track.mp3 lautet, sollte die Ausgabe folgendermaßen aussehen:
Playing MP3: favorite_track.mp3Das Schöne an diesem Pattern ist, dass dein Hauptcode nur mit IMediaPlayer interagiert – er weiß nicht, dass eine ältere Audiobibliothek die eigentliche Arbeit erledigt. Wenn du später eine andere ältere Bibliothek unterstützen musst, erstellst du einfach einen neuen Adapter, ohne Client-Code ändern zu müssen!
Probier es selbst
using System;
using MediaSystem;
class Program
{
public static void Main(string[] args)
{
// Lies die Dateinamen-Eingabe ein
string filename = Console.ReadLine();
// TODO: Erstelle eine LegacyAudioPlayer-Instanz
// TODO: Umschließe sie mit einem MediaPlayerAdapter
// TODO: Verwende den Adapter über das IMediaPlayer-Interface
// und gib das Ergebnis des Aufrufs von Play mit dem Dateinamen aus
}
}
Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Objektorientierte Programmierung
1Grundlagen der OOP
Externe DateienNamespaces & DirektivenEinführung in Klassen & ObjekteDas 'this'-SchlüsselwortMethoden und ParameterFelder vs. EigenschaftenKonstruktorenObjektinitialisiererWiederholung – Einfacher Taschenrechner4Vererbung
Grundlegende Vererbungs-(:)-SyntaxDas „base“-Schlüsselwort„virtual“- und „override“-SchlüsselwörterVersiegelte KlassenDie „object“-BasisklasseWiederholung – Mitarbeiterhierarchie7Erweiterte Funktionen
Überladen von OperatorenIndexer (this[])ToString()-ÜberschreibungErweiterungsmethodenRückblick – Benutzerdefinierte Liste10Entwurfsmuster Teil 1
Einführung in EntwurfsmusterThread-sicheres SingletonFactory-MusterObserver-Muster (Events)Strategy-Muster13Abschließende Herausforderungen
E-Learning-PlattformKomponente für SpielfigurenFahrzeugvermietungsdienst2Eigenschaften & statische Member
Automatisch implementierte EigenschaftenNur lesbare/schreibbare EigenschaftenStatische Felder & MethodenStatische KlassenMember mit Ausdruckskörpern5Polymorphie & Interfaces
Compile- vs. Laufzeit-PolymorphieInterface vs. abstrakte KlasseMehrere InterfacesExplizite InterfacesUpcasting & DowncastingWiederholung – Formenrechner8Fortgeschrittene OOP-Konzepte
Komposition statt VererbungGenerics (Klassen & Methoden)Delegates und EventsAttribute und ReflectionIDisposable & using-AnweisungGrundlagen der Dependency Injection11Entwurfsmuster Teil 2
BefehlsmusterAdaptermusterDekorierermusterVorlagenmethodenmusterZustandsmusterKompositumsmuster3Klassenarchitektur
Instanz- vs. statische DatenSchlüsselwörter 'readonly' & 'const'Backing FieldsRückblick – Bankkontenverwaltung6Kapselung
ZugriffsmodifikatorenEigenschaften zur KapselungImplementierung der DatenkapselungMuster für UnveränderlichkeitRückblick – SchülerdatenÜbe selbstständig: Online-C#-Compiler