Menu
Coddy logo textTech

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.mp4

Kernpunkt: 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.

challenge icon

Aufgabe

Mittel

In 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:

  1. Die in interfaces.py definierten modernen Schnittstellen verstehen (diese Datei kann nicht geändert werden)
  2. Die Legacy-Komponenten in legacy_system.py untersuchen (diese Datei kann nicht geändert werden)
  3. Adapter-Klassen in adapters.py implementieren, welche die Legacy-Komponenten mit den modernen Schnittstellen kompatibel machen
  4. Ein modernes System in modern_system.py erstellen, 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: Hello

Beispiel 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())
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 Object Oriented Programming