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.mp4Ponto 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.
Desafio
MédioNeste 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:
- Entender as interfaces modernas definidas em
interfaces.py(este arquivo não pode ser modificado) - Examinar os componentes legados em
legacy_system.py(este arquivo não pode ser modificado) - Implementar classes adaptadoras em
adapters.pyque tornem os componentes legados compatíveis com as interfaces modernas - Criar um sistema moderno em
modern_system.pyque 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: HelloExemplo 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())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
1Fundamentos de POO
Arquivos ExternosIntrodução à POOClasses vs ObjetosO Parâmetro selfMétodosAtributosMétodo Construtor (__init__)Recapitulação - Calculadora Simples4Herança
Herança BásicaA Função super()Sobrescrita de MétodoHerança MúltiplaOrdem de Resolução de MétodosRecapitulação - Hierarquia de Funcionários7Métodos Especiais
Introdução aos Métodos MágicosSobrecarga de OperadoresMétodos Mágicos de ContêinerRecapitulação - Lista Personalizada10Padrões de Projeto Parte 1
Introdução a Padrões de ProjetoPadrão SingletonPadrão FactoryPadrão ObserverPadrão Strategy2Decoradores
Introdução aos DecoradoresDecorador de PropriedadeDecorador de Método EstáticoDecorador de Método de Classe5Polimorfismo
Sobrescrita de Método RevisitadaDuck TypingClasses e Métodos AbstratosDesign de InterfacesRecapitulação - Calculadora de Formas8Conceitos Avançados de POO
Composição vs HerançaMixinsMétodos Estáticos e de ClasseDecoradores de ClasseGerenciadores de Contexto11Padrões de Projeto Parte 2
Padrão CommandPadrão AdapterPadrão DecoratorPadrão Template MethodPadrão StatePadrão Composite3Propriedades de Classe
Variáveis de Instância vs de ClasseDecoradores de PropriedadeAtributos PrivadosRecapitulação - Gerenciador de Conta Bancária6Encapsulamento
Membros Públicos, Protegidos e PrivadosModificadores de AcessoOcultação de InformaçãoProperty Decorators AvançadosRecapitulação - Sistema de Registro de Alunos12Projeto: Gerenciamento de Biblioteca
Visão Geral do ProjetoClasses Livro e Usuário