Menu
Coddy logo textTech

Patrón Adapter

Parte de la sección Programación orientada a objetos del Journey de Python de Coddy. Lección 50 de 64.

El patrón Adapter permite que objetos con interfaces incompatibles trabajen juntos. Actúa como un puente al envolver una clase existente con una nueva interfaz que los clientes esperan.

Aquí hay dos sistemas con interfaces incompatibles:

class OldPrinter:
    def old_print(self, text):
        return f"OLD: {text}"

class NewPrinter:
    def print(self, text):
        return f"NEW: {text}"

La impresora antigua usa old_print(), mientras que la nueva usa print().

Crea un adaptador para hacer que la impresora antigua funcione con la nueva interfaz:

class PrinterAdapter:
    def __init__(self, old_printer):
        self.old_printer = old_printer
    
    def print(self, text):
        # Adaptar la interfaz antigua a la nueva interfaz
        return self.old_printer.old_print(text)

El adaptador envuelve la impresora antigua y proporciona la interfaz esperada.

Usa ambas impresoras con el mismo código del cliente:

def print_document(printer, text):
    return printer.print(text)  # Espera el método print()

# Usa la nueva impresora directamente
new_printer = NewPrinter()
print(print_document(new_printer, "Hello"))

# Usa la impresora antigua a través del adaptador
old_printer = OldPrinter()
adapter = PrinterAdapter(old_printer)
print(print_document(adapter, "Hello"))

Crea otro ejemplo con reproductores multimedia:

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"))

Salida:

NEW: Hello
OLD: Hello
Playing MP3: song.mp3
Playing MP4: video.mp4

Punto clave: el patrón Adaptador hace que las interfaces incompatibles funcionen juntas al envolver una clase existente con una nueva interfaz. El adaptador traduce las llamadas de la interfaz esperada a la interfaz real del objeto envuelto. Esto resulta útil para integrar código heredado o bibliotecas de terceros sin modificar el código existente.

challenge icon

Desafío

Intermedio

En este desafío, implementarás el patrón de diseño Adapter para integrar sistemas heredados con una arquitectura de aplicaciones moderna. El patrón Adapter permite que objetos con interfaces incompatibles trabajen juntos mediante la creación de un envoltorio (adaptador) que traduce una interfaz a otra.

 

Tu empresa tiene un sistema heredado de análisis y visualización de datos que necesita integrarse con una nueva plataforma moderna de análisis. Los componentes heredados tienen interfaces incompatibles con el nuevo sistema. En lugar de reescribir el código heredado (lo que sería arriesgado y costoso), se te ha encargado crear adaptadores para hacer que estos componentes funcionen con el nuevo sistema.

Debes:

  1. Comprender las interfaces modernas definidas en interfaces.py (este archivo no se puede modificar)
  2. Examinar los componentes heredados en legacy_system.py (este archivo no se puede modificar)
  3. Implementar clases adaptadoras en adapters.py que hagan que los componentes heredados sean compatibles con las interfaces modernas
  4. Crear un sistema moderno en modern_system.py que utilice estas interfaces

Pruébalo tú mismo

# Importar todas las clases necesarias
from adapters import LegacyDataAnalyzerAdapter, LegacyChartGeneratorAdapter
from modern_system import AnalyticsSystem

# Manejador integral de casos de prueba
test_case = input()

if test_case == "basic_adapter_test":
    # Probar la funcionalidad básica del adaptador
    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":
    # Probar el adaptador del visualizador
    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":
    # Probar el sistema de análisis completo
    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":
    # Test validation errors
    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":
    # Probar con datos vacíos
    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":
    # Probar diferentes tipos de gráficos
    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":
    # Probar el manejo de extensiones de archivo
    visualizer = LegacyChartGeneratorAdapter()
    data = [1, 2, 3, 4]
    visualizer.visualize(data)
    
    # Probar varios formatos de nombre de archivo
    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":
    # Test analysis summary
    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":
    # Probar con datos numéricos mixtos
    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":
    # Probar con datos numéricos no válidos
    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":
    # Probar el resumen sin resultados
    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":
    # Prueba con un conjunto de datos más grande
    processor = LegacyDataAnalyzerAdapter()
    data = list(range(1, 101))  # 1 a 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":
    # Test complete workflow with multiple operations
    processor = LegacyDataAnalyzerAdapter()
    bar_visualizer = LegacyChartGeneratorAdapter("bar")
    line_visualizer = LegacyChartGeneratorAdapter("line")
    
    data = [12, 18, 25, 32, 28, 35, 42]
    
    # Crea sistemas de analítica con diferentes visualizadores
    bar_system = AnalyticsSystem(processor, bar_visualizer)
    line_system = AnalyticsSystem(LegacyDataAnalyzerAdapter(), line_visualizer)
    
    # Ejecuta los análisis
    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']}")
    
    # Obtén los resúmenes
    print("Bar system summary:")
    print(bar_system.get_analysis_summary())
    print("Line system summary:")
    print(line_system.get_analysis_summary())
quiz iconPonte a prueba

Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.

Todas las lecciones de Programación orientada a objetos

Practica por tu cuenta: Compilador de Python online