Pattern adattatore
Fa parte della sezione Programmazione orientata agli oggetti del percorso Python di Coddy. Lezione 50 di 64.
Il pattern Adapter consente a oggetti con interfacce incompatibili di collaborare. Funge da ponte avvolgendo una classe esistente con una nuova interfaccia, quella attesa dai client.
Ecco due sistemi con interfacce incompatibili:
class OldPrinter:
def old_print(self, text):
return f"OLD: {text}"
class NewPrinter:
def print(self, text):
return f"NEW: {text}"La vecchia stampante usa old_print(), mentre quella nuova usa print().
Crea un adattatore per far funzionare la vecchia stampante con la nuova interfaccia:
class PrinterAdapter:
def __init__(self, old_printer):
self.old_printer = old_printer
def print(self, text):
# Adatta la vecchia interfaccia alla nuova interfaccia
return self.old_printer.old_print(text)L’adattatore avvolge la vecchia stampante e fornisce l’interfaccia prevista.
Usa entrambe le stampanti con lo stesso codice client:
def print_document(printer, text):
return printer.print(text) # Si aspetta il metodo print()
# Usa direttamente la nuova stampante
new_printer = NewPrinter()
print(print_document(new_printer, "Hello"))
# Usa la vecchia stampante tramite l'adattatore
old_printer = OldPrinter()
adapter = PrinterAdapter(old_printer)
print(print_document(adapter, "Hello"))Crea un altro esempio con lettori multimediali:
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"))Output:
NEW: Hello
OLD: Hello
Playing MP3: song.mp3
Playing MP4: video.mp4Punto chiave: il pattern Adapter consente a interfacce incompatibili di funzionare insieme avvolgendo una classe esistente con una nuova interfaccia. L’adattatore traduce le chiamate dall’interfaccia prevista all’interfaccia effettiva dell’oggetto avvolto. È utile per integrare codice legacy o librerie di terze parti senza modificare il codice esistente.
Sfida
MedioIn questa sfida, implementerai il pattern di progettazione Adapter per integrare sistemi legacy con un’architettura applicativa moderna. Il pattern Adapter consente a oggetti con interfacce incompatibili di collaborare creando un wrapper (adapter) che traduce un’interfaccia nell’altra.
La tua azienda dispone di un sistema legacy di analisi e visualizzazione dei dati che deve essere integrato con una nuova piattaforma moderna di analisi. I componenti legacy hanno interfacce incompatibili con il nuovo sistema. Invece di riscrivere il codice legacy (un’operazione rischiosa e costosa), ti è stato affidato il compito di creare degli adapter per rendere questi componenti compatibili con il nuovo sistema.
Devi:
- Comprendere le interfacce moderne definite in
interfaces.py(questo file non può essere modificato) - Esaminare i componenti legacy in
legacy_system.py(questo file non può essere modificato) - Implementare classi adapter in
adapters.pyper rendere i componenti legacy compatibili con le interfacce moderne - Creare un sistema moderno in
modern_system.pyche utilizzi queste interfacce
Provalo tu
# Importa tutte le classi necessarie
from adapters import LegacyDataAnalyzerAdapter, LegacyChartGeneratorAdapter
from modern_system import AnalyticsSystem
# Gestore completo dei casi di test
test_case = input()
if test_case == "basic_adapter_test":
# Verifica le funzionalità di base dell'adapter
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":
# Verifica l'adapter per la visualizzazione
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":
# Verifica l'intero sistema di analisi
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":
# Verifica gli errori di validazione
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":
# Verifica il comportamento con dati vuoti
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":
# Verifica diversi tipi di grafico
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":
# Verifica la gestione delle estensioni dei file
visualizer = LegacyChartGeneratorAdapter()
data = [1, 2, 3, 4]
visualizer.visualize(data)
# Verifica diversi formati dei nomi di file
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":
# Verifica il riepilogo dell'analisi
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":
# Verifica il comportamento con dati numerici misti
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":
# Verifica il comportamento con dati numerici non validi
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":
# Verifica il riepilogo in assenza di risultati
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":
# Verifica il comportamento con un set di dati più grande
processor = LegacyDataAnalyzerAdapter()
data = list(range(1, 101)) # da 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":
# Verifica il flusso di lavoro completo con più operazioni
processor = LegacyDataAnalyzerAdapter()
bar_visualizer = LegacyChartGeneratorAdapter("bar")
line_visualizer = LegacyChartGeneratorAdapter("line")
data = [12, 18, 25, 32, 28, 35, 42]
# Crea sistemi di analisi con diversi visualizzatori
bar_system = AnalyticsSystem(processor, bar_visualizer)
line_system = AnalyticsSystem(LegacyDataAnalyzerAdapter(), line_visualizer)
# Esegui le analisi
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']}")
# Ottieni i riepiloghi
print("Bar system summary:")
print(bar_system.get_analysis_summary())
print("Line system summary:")
print(line_system.get_analysis_summary())Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.
Tutte le lezioni di Programmazione orientata agli oggetti
1Fondamenti di OOP
File esterniIntroduzione a OOPClassi vs. oggettiIl parametro selfMetodiAttributiMetodo costruttore (__init__)Riepilogo - Calcolatrice semplice4Ereditarietà
Ereditarietà di baseLa funzione super()Sovrascrittura dei metodiEreditarietà multiplaOrdine di risoluzione dei metodiRiepilogo - Gerarchia dei dipendenti7Metodi speciali
Introduzione ai metodi magiciSovraccarico degli operatoriMetodi magici dei contenitoriRiepilogo - Lista personalizzata10Modelli di progettazione Parte 1
Introduzione ai modelli di progettazioneModello SingletonModello FactoryModello ObserverModello Strategy2Decoratori
Introduzione ai decoratoriDecoratore di proprietàDecoratore di metodo staticoDecoratore di metodo di classe5Polimorfismo
Ripasso dell'override dei metodiDuck typingClassi e metodi astrattiProgettazione delle interfacceRiepilogo - Calcolatore di forme8Concetti avanzati di OOP
Composizione vs ereditarietàMixinMetodi statici e di classeDecorator di classeContext manager11Pattern di progettazione - Parte 2
Pattern dei comandiPattern adattatorePattern decoratorePattern metodo templatePattern statoPattern composito3Proprietà delle classi
Variabili di istanza e di classeDecorator delle proprietàAttributi privatiRiepilogo - Gestore di conti bancari6Incapsulamento
Membri pubblici, protetti e privatiModificatori di accessoOccultamento delle informazioniDecorator delle proprietà: avanzatiRipasso - Sistema di registri degli studenti12Progetto: Gestione della biblioteca
Panoramica del progettoClassi Libro e UtenteSistema di prestitoFunzionalità di ricercaInterfaccia amministratoreTest e integrazioneEsercitati da solo: Compilatore Python online