Menu
Coddy logo textTech

Padrão Adapter

Parte da seção Object Oriented Programming do Journey de Python da Coddy — lição 50 de 64.

O Padrão Adapter permite que objetos com interfaces incompatíveis trabalhem juntos. Ele atua como uma ponte ao envolver uma classe existente com uma nova interface que os clientes esperam.

Aqui estão dois sistemas com interfaces incompatíveis:

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

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

A impressora antiga usa old_print() enquanto a nova usa print().

Crie um adaptador para fazer a impressora antiga funcionar com a nova interface:

class PrinterAdapter:
    def __init__(self, old_printer):
        self.old_printer = old_printer
    
    def print(self, text):
        # Adapta a interface antiga para a nova interface
        return self.old_printer.old_print(text)

O adaptador envolve a impressora antiga e fornece a interface esperada.

Use ambas as impressoras com o mesmo código de cliente:

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

# Usa a nova impressora diretamente
new_printer = NewPrinter()
print(print_document(new_printer, "Hello"))

# Usa a impressora antiga através do adaptador
old_printer = OldPrinter()
adapter = PrinterAdapter(old_printer)
print(print_document(adapter, "Hello"))

Crie outro exemplo com reprodutores de mídia:

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

Saída:

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

Ponto Chave: O Padrão Adapter faz com que interfaces incompatíveis trabalhem juntas ao envolver uma classe existente com uma nova interface. O adapter traduz as chamadas da interface esperada para a interface real do objeto envolvido. Isso é útil para integrar código legado ou bibliotecas de terceiros sem modificar o código existente.

challenge icon

Desafio

Médio

Neste desafio, você implementará o padrão de design Adapter para integrar sistemas legados com uma arquitetura de aplicação moderna. O padrão Adapter permite que objetos com interfaces incompatíveis trabalhem juntos, criando um invólucro (adapter) que traduz uma interface para outra.

 

Sua empresa possui um sistema legado de análise e visualização de dados que precisa ser integrado a uma nova plataforma de análise moderna. Os componentes legados possuem interfaces que são incompatíveis com o novo sistema. Em vez de reescrever o código legado (o que seria arriscado e caro), você foi encarregado de criar adaptadores para fazer esses componentes funcionarem com o novo sistema.

Você precisa:

  1. Entender as interfaces modernas definidas em interfaces.py (este arquivo não pode ser modificado)
  2. Examinar os componentes legados em legacy_system.py (este arquivo não pode ser modificado)
  3. Implementar classes adaptadoras em adapters.py que tornem os componentes legados compatíveis com as interfaces modernas
  4. Criar um sistema moderno em modern_system.py que utilize essas interfaces

Folha de consulta

O Padrão Adapter permite que objetos com interfaces incompatíveis trabalhem juntos, envolvendo uma classe existente com uma nova interface que os clientes esperam.

Estrutura básica do adapter:

class PrinterAdapter:
    def __init__(self, old_printer):
        self.old_printer = old_printer
    
    def print(self, text):
        # Adapta a interface antiga para a nova interface
        return self.old_printer.old_print(text)

Exemplo com interfaces de impressora incompatíveis:

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

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

# O código do cliente espera o método print()
def print_document(printer, text):
    return printer.print(text)

# Usa a impressora antiga através do adapter
old_printer = OldPrinter()
adapter = PrinterAdapter(old_printer)
print(print_document(adapter, "Hello"))  # OLD: Hello

Exemplo de adapter de player de mídia:

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)

O adapter traduz chamadas da interface esperada para a interface real do objeto envolvido, permitindo a integração de código legado sem modificação.

Experimente você mesmo

# Importa todas as classes necessárias
from adapters import LegacyDataAnalyzerAdapter, LegacyChartGeneratorAdapter
from modern_system import AnalyticsSystem

# Manipulador abrangente de casos de teste
test_case = input()

if test_case == "basic_adapter_test":
    # Testa a funcionalidade básica do 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":
    # Testa o adaptador do 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":
    # Testa o sistema de análise 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":
    # Testa erros de validação
    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":
    # Testa com dados vazios
    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":
    # Testa 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":
    # Testa o tratamento de extensões de arquivo
    visualizer = LegacyChartGeneratorAdapter()
    data = [1, 2, 3, 4]
    visualizer.visualize(data)
    
    # Testa vários formatos de nome de arquivo
    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":
    # Testa o resumo da análise
    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":
    # Testa com dados numéricos mistos
    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":
    # Testa com dados numéricos invá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":
    # Testa o resumo sem 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":
    # Testa com um conjunto de dados maior
    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":
    # Testa o fluxo de trabalho completo com múltiplas operações
    processor = LegacyDataAnalyzerAdapter()
    bar_visualizer = LegacyChartGeneratorAdapter("bar")
    line_visualizer = LegacyChartGeneratorAdapter("line")
    
    data = [12, 18, 25, 32, 28, 35, 42]
    
    # Cria sistemas de análise com diferentes visualizadores
    bar_system = AnalyticsSystem(processor, bar_visualizer)
    line_system = AnalyticsSystem(LegacyDataAnalyzerAdapter(), line_visualizer)
    
    # Executa as análises
    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ém os resumos
    print("Bar system summary:")
    print(bar_system.get_analysis_summary())
    print("Line system summary:")
    print(line_system.get_analysis_summary())
quiz iconTeste seus conhecimentos

Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.

Todas as lições de Object Oriented Programming