Adapter-Muster
Teil des Abschnitts Object Oriented Programming der Python-Journey von Coddy — Lektion 50 von 64.
Das Adapter-Muster ermöglicht es Objekten mit inkompatiblen Schnittstellen, zusammenzuarbeiten. Es fungiert als Brücke, indem es eine bestehende Klasse mit einer neuen Schnittstelle umhüllt, die Clients erwarten.
Hier sind zwei Systeme mit inkompatiblen Schnittstellen:
class OldPrinter:
def old_print(self, text):
return f"OLD: {text}"
class NewPrinter:
def print(self, text):
return f"NEW: {text}"Der alte Drucker verwendet old_print(), während der neue print() verwendet.
Erstellen Sie einen Adapter, damit der alte Drucker mit der neuen Schnittstelle funktioniert:
class PrinterAdapter:
def __init__(self, old_printer):
self.old_printer = old_printer
def print(self, text):
# Adapt old interface to new interface
return self.old_printer.old_print(text)Der Adapter umschließt den alten Drucker und stellt die erwartete Schnittstelle bereit.
Verwenden Sie beide Drucker mit demselben Client-Code:
def print_document(printer, text):
return printer.print(text) # Erwartet die print()-Methode
# Neuen Drucker direkt verwenden
new_printer = NewPrinter()
print(print_document(new_printer, "Hello"))
# Alten Drucker über Adapter verwenden
old_printer = OldPrinter()
adapter = PrinterAdapter(old_printer)
print(print_document(adapter, "Hello"))Erstellen Sie ein weiteres Beispiel mit Media-Playern:
class Mp3Player:
def play_mp3(self, filename):
return f"Playing MP3: {filename}"
class Mp4Player:
def play_mp4(self, filename):
return f"Playing MP4: {filename}"
class MediaAdapter:
def __init__(self, player, audio_type):
self.player = player
self.audio_type = audio_type
def play(self, filename):
if self.audio_type == "mp3":
return self.player.play_mp3(filename)
elif self.audio_type == "mp4":
return self.player.play_mp4(filename)class AudioPlayer:
def play(self, audio_type, filename):
if audio_type == "mp3":
return Mp3Player().play_mp3(filename)
else:
adapter = MediaAdapter(Mp4Player(), audio_type)
return adapter.play(filename)
player = AudioPlayer()
print(player.play("mp3", "song.mp3"))
print(player.play("mp4", "video.mp4"))Ausgabe:
NEW: Hello
OLD: Hello
Playing MP3: song.mp3
Playing MP4: video.mp4Kernpunkt: Das Adapter-Muster lässt inkompatible Schnittstellen zusammenarbeiten, indem es eine bestehende Klasse mit einer neuen Schnittstelle umhüllt. Der Adapter übersetzt Aufrufe von der erwarteten Schnittstelle in die tatsächliche Schnittstelle des umhüllten Objekts. Dies ist nützlich für die Integration von Legacy-Code oder Bibliotheken von Drittanbietern, ohne den bestehenden Code zu ändern.
Aufgabe
MittelIn dieser Herausforderung werden Sie das Adapter-Entwurfsmuster implementieren, um Altsysteme in eine moderne Anwendungsarchitektur zu integrieren. Das Adapter-Muster ermöglicht es Objekten mit inkompatiblen Schnittstellen, zusammenzuarbeiten, indem ein Wrapper (Adapter) erstellt wird, der eine Schnittstelle in eine andere übersetzt.
Ihr Unternehmen verfügt über ein veraltetes Datenanalyse- und Visualisierungssystem, das in eine neue moderne Analyseplattform integriert werden muss. Die Legacy-Komponenten verfügen über Schnittstellen, die mit dem neuen System inkompatibel sind. Anstatt den Legacy-Code neu zu schreiben (was riskant und teuer wäre), wurden Sie damit beauftragt, Adapter zu erstellen, damit diese Komponenten mit dem neuen System funktionieren.
Sie müssen:
- Die in
interfaces.pydefinierten modernen Schnittstellen verstehen (diese Datei kann nicht geändert werden) - Die Legacy-Komponenten in
legacy_system.pyuntersuchen (diese Datei kann nicht geändert werden) - Adapter-Klassen in
adapters.pyimplementieren, welche die Legacy-Komponenten mit den modernen Schnittstellen kompatibel machen - Ein modernes System in
modern_system.pyerstellen, das diese Schnittstellen verwendet
Spickzettel
Das Adapter-Muster ermöglicht es Objekten mit inkompatiblen Schnittstellen zusammenzuarbeiten, indem eine bestehende Klasse mit einer neuen Schnittstelle umhüllt wird, die von den Clients erwartet wird.
Grundlegende Adapter-Struktur:
class PrinterAdapter:
def __init__(self, old_printer):
self.old_printer = old_printer
def print(self, text):
# Alte Schnittstelle an neue Schnittstelle anpassen
return self.old_printer.old_print(text)Beispiel mit inkompatiblen Drucker-Schnittstellen:
class OldPrinter:
def old_print(self, text):
return f"OLD: {text}"
class NewPrinter:
def print(self, text):
return f"NEW: {text}"
# Client-Code erwartet die print()-Methode
def print_document(printer, text):
return printer.print(text)
# Alten Drucker über Adapter verwenden
old_printer = OldPrinter()
adapter = PrinterAdapter(old_printer)
print(print_document(adapter, "Hello")) # OLD: HelloBeispiel für einen Media-Player-Adapter:
class MediaAdapter:
def __init__(self, player, audio_type):
self.player = player
self.audio_type = audio_type
def play(self, filename):
if self.audio_type == "mp3":
return self.player.play_mp3(filename)
elif self.audio_type == "mp4":
return self.player.play_mp4(filename)Der Adapter übersetzt Aufrufe von der erwarteten Schnittstelle in die tatsächliche Schnittstelle des umhüllten Objekts und ermöglicht so die Integration von Legacy-Code ohne Modifikation.
Probier es selbst
# Importiere alle notwendigen Klassen
from adapters import LegacyDataAnalyzerAdapter, LegacyChartGeneratorAdapter
from modern_system import AnalyticsSystem
# Handler für umfassende Testfälle
test_case = input()
if test_case == "basic_adapter_test":
# Teste grundlegende Adapter-Funktionalität
processor = LegacyDataAnalyzerAdapter()
data = [1, 2, 3, 4, 5]
processor.process_data(data)
results = processor.get_results()
print(f"Results: {results}")
elif test_case == "visualizer_adapter_test":
# Teste Visualisierungs-Adapter
visualizer = LegacyChartGeneratorAdapter("bar")
data = [10, 20, 30, 40, 50]
success = visualizer.visualize(data)
if success:
export_success = visualizer.export_visualization("test_chart.png")
print(f"Visualization exported: {export_success}")
elif test_case == "analytics_system_test":
# Teste komplettes Analysesystem
processor = LegacyDataAnalyzerAdapter()
visualizer = LegacyChartGeneratorAdapter("line")
system = AnalyticsSystem(processor, visualizer)
data = [15, 25, 35, 45, 55]
result = system.analyze_and_visualize(data, "analytics_output.png")
print(f"System result: {result}")
elif test_case == "validation_error_test":
# Teste Validierungsfehler
processor = LegacyDataAnalyzerAdapter()
try:
processor.process_data("invalid_data")
print("Validation failed - should have raised ValueError")
except ValueError as e:
print(f"Validation error caught: {e}")
elif test_case == "empty_data_test":
# Teste mit leeren Daten
processor = LegacyDataAnalyzerAdapter()
try:
processor.process_data([])
results = processor.get_results()
print(f"Empty data results: {results}")
except Exception as e:
print(f"Empty data error: {e}")
elif test_case == "chart_types_test":
# Teste verschiedene Diagrammtypen
chart_types = ["bar", "line", "pie"]
data = [5, 15, 25, 35]
for chart_type in chart_types:
visualizer = LegacyChartGeneratorAdapter(chart_type)
success = visualizer.visualize(data)
print(f"{chart_type} chart created: {success}")
elif test_case == "file_extension_test":
# Teste Handhabung von Dateiendungen
visualizer = LegacyChartGeneratorAdapter()
data = [1, 2, 3, 4]
visualizer.visualize(data)
# Teste verschiedene Dateiformate
filenames = ["test", "test.jpg", "test.pdf", "test.png"]
for filename in filenames:
success = visualizer.export_visualization(filename)
print(f"Export '{filename}': {success}")
elif test_case == "summary_test":
# Teste Zusammenfassung der Analyse
processor = LegacyDataAnalyzerAdapter()
visualizer = LegacyChartGeneratorAdapter()
system = AnalyticsSystem(processor, visualizer)
data = [10, 20, 30, 40, 50, 60]
system.analyze_and_visualize(data)
summary = system.get_analysis_summary()
print("Analysis Summary:")
print(summary)
elif test_case == "mixed_data_test":
# Teste mit gemischten numerischen Daten
processor = LegacyDataAnalyzerAdapter()
data = [1.5, 2, 3.7, 4, 5.2]
processor.process_data(data)
results = processor.get_results()
print(f"Mixed data results: {results}")
elif test_case == "invalid_numeric_test":
# Teste mit ungültigen numerischen Daten
processor = LegacyDataAnalyzerAdapter()
try:
processor.process_data([1, 2, "three", 4, 5])
print("Validation failed - should have raised ValueError")
except ValueError as e:
print(f"Invalid numeric data error: {e}")
elif test_case == "no_results_summary_test":
# Teste Zusammenfassung ohne Ergebnisse
processor = LegacyDataAnalyzerAdapter()
visualizer = LegacyChartGeneratorAdapter()
system = AnalyticsSystem(processor, visualizer)
summary = system.get_analysis_summary()
print(f"No results summary: {summary}")
elif test_case == "large_dataset_test":
# Teste mit größerem Datensatz
processor = LegacyDataAnalyzerAdapter()
data = list(range(1, 101)) # 1 bis 100
processor.process_data(data)
results = processor.get_results()
print(f"Large dataset - Mean: {results.get('mean', 'N/A'):.2f}")
print(f"Large dataset - Min: {results.get('min', 'N/A')}")
print(f"Large dataset - Max: {results.get('max', 'N/A')}")
elif test_case == "complete_workflow_test":
# Teste kompletten Workflow mit mehreren Operationen
processor = LegacyDataAnalyzerAdapter()
bar_visualizer = LegacyChartGeneratorAdapter("bar")
line_visualizer = LegacyChartGeneratorAdapter("line")
data = [12, 18, 25, 32, 28, 35, 42]
# Erstelle Analysesysteme mit verschiedenen Visualisierern
bar_system = AnalyticsSystem(processor, bar_visualizer)
line_system = AnalyticsSystem(LegacyDataAnalyzerAdapter(), line_visualizer)
# Analysen ausführen
bar_result = bar_system.analyze_and_visualize(data, "bar_chart.png")
line_result = line_system.analyze_and_visualize(data, "line_chart.png")
print("Complete workflow test completed")
print(f"Bar chart result: {bar_result['visualization_file']}")
print(f"Line chart result: {line_result['visualization_file']}")
# Zusammenfassungen abrufen
print("Bar system summary:")
print(bar_system.get_analysis_summary())
print("Line system summary:")
print(line_system.get_analysis_summary())Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Object Oriented Programming
1Grundlagen der OOP
Externe DateienEinführung in die OOPKlassen vs. ObjekteDer self-ParameterMethodenAttributeKonstruktor-Methode (__init__)Zusammenfassung – Einfacher Taschenrechner4Vererbung
Grundlagen der VererbungDie super()-FunktionMethoden überschreibenMehrfachvererbungMethod Resolution OrderZusammenfassung - Mitarbeiter-Hierarchie7Spezielle Methoden
Einführung in Magic MethodsOperator-ÜberladungMagic Methods für ContainerRückblick - Eigene Liste10Entwurfsmuster Teil 1
Einführung in EntwurfsmusterSingleton-MusterFactory-MusterObserver-MusterStrategy-Muster13Abschlussherausforderungen
E-Learning-PlattformBanksystemSpielcharakter-EntwicklungFahrzeugvermietung2Dekoratoren
Einführung in DekoratorenProperty-DekoratorStatischer Methoden-DekoratorKlassenmethoden-Dekorator5Polymorphismus
Methoden-Überschreiben vertieftDuck TypingAbstrakte Klassen und MethodenInterface-DesignZusammenfassung – Formen-Rechner8Fortgeschrittene OOP-Konzepte
Komposition vs. VererbungMixinsStatische und KlassenmethodenKlassendekoratorenContext Manager11Entwurfsmuster Teil 2
Command-MusterAdapter-MusterDecorator-MusterTemplate-Method-MusterZustandsmusterKompositum-Muster3Klasseneigenschaften
Instanz- vs. KlassenvariablenProperty-DekoratorenPrivate AttributeZusammenfassung – Bankkonto-Manager6Kapselung
Public, Protected, Private MemberZugriffsmodifikatorenInformation HidingProperty-Decorators für FortgeschritteneZusammenfassung - Studentenverwaltungssystem12Projekt: Bibliotheksverwaltung
ProjektübersichtBuch- und Benutzerklassen