Patron Adaptateur
Fait partie de la section Programmation orientée objet du Journey Python de Coddy. Leçon 50 sur 64.
Le pattern Adapter permet à des objets dotés d’interfaces incompatibles de fonctionner ensemble. Il agit comme un pont en enveloppant une classe existante avec une nouvelle interface attendue par les clients.
Voici deux systèmes aux interfaces incompatibles :
class OldPrinter:
def old_print(self, text):
return f"OLD: {text}"
class NewPrinter:
def print(self, text):
return f"NEW: {text}"L’ancienne imprimante utilise old_print(), tandis que la nouvelle utilise print().
Créez un adaptateur pour faire fonctionner l’ancienne imprimante avec la nouvelle interface :
class PrinterAdapter:
def __init__(self, old_printer):
self.old_printer = old_printer
def print(self, text):
# Adapter l'ancienne interface à la nouvelle interface
return self.old_printer.old_print(text)L’adaptateur encapsule l’ancienne imprimante et fournit l’interface attendue.
Utilisez les deux imprimantes avec le même code client :
def print_document(printer, text):
return printer.print(text) # Attend la méthode print()
# Utiliser la nouvelle imprimante directement
new_printer = NewPrinter()
print(print_document(new_printer, "Hello"))
# Utiliser l'ancienne imprimante via l'adaptateur
old_printer = OldPrinter()
adapter = PrinterAdapter(old_printer)
print(print_document(adapter, "Hello"))Créez un autre exemple avec des lecteurs multimédias :
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"))Sortie :
NEW: Hello
OLD: Hello
Playing MP3: song.mp3
Playing MP4: video.mp4Point clé : le pattern Adapter permet à des interfaces incompatibles de fonctionner ensemble en encapsulant une classe existante avec une nouvelle interface. L’adaptateur traduit les appels de l’interface attendue vers l’interface réelle de l’objet encapsulé. Cela est utile pour intégrer du code existant ou des bibliothèques tierces sans modifier le code existant.
Défi
MoyenDans ce défi, vous allez implémenter le modèle de conception Adapter afin d’intégrer des systèmes existants à une architecture d’application moderne. Le modèle Adapter permet à des objets dotés d’interfaces incompatibles de fonctionner ensemble en créant un wrapper (adaptateur) qui traduit une interface en une autre.
Votre entreprise dispose d’un ancien système d’analyse et de visualisation des données qui doit être intégré à une nouvelle plateforme d’analyse moderne. Les composants existants possèdent des interfaces incompatibles avec le nouveau système. Plutôt que de réécrire le code existant (ce qui serait risqué et coûteux), vous avez pour mission de créer des adaptateurs afin de faire fonctionner ces composants avec le nouveau système.
Vous devez :
- Comprendre les interfaces modernes définies dans
interfaces.py(ce fichier ne peut pas être modifié) - Examiner les composants existants dans
legacy_system.py(ce fichier ne peut pas être modifié) - Implémenter des classes d’adaptateur dans
adapters.pyafin de rendre les composants existants compatibles avec les interfaces modernes - Créer un système moderne dans
modern_system.pyqui utilise ces interfaces
Essayez vous-même
# Importer toutes les classes nécessaires
from adapters import LegacyDataAnalyzerAdapter, LegacyChartGeneratorAdapter
from modern_system import AnalyticsSystem
# Gestionnaire complet de cas de test
test_case = input()
if test_case == "basic_adapter_test":
# Tester la fonctionnalité de base de l'adaptateur
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":
# Tester l'adaptateur de visualisation
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":
# Tester le système d'analyse complet
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":
# Tester avec des données vides
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":
# Tester différents types de graphiques
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":
# Tester la gestion des extensions de fichier
visualizer = LegacyChartGeneratorAdapter()
data = [1, 2, 3, 4]
visualizer.visualize(data)
# Tester divers formats de nom de fichier
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":
# Tester avec des données numériques mixtes
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":
# Tester avec des données numériques invalides
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":
# Tester le résumé sans résultats
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":
# Tester avec un ensemble de données plus large
processor = LegacyDataAnalyzerAdapter()
data = list(range(1, 101)) # 1 à 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]
# Créer des systèmes d'analyse avec différents visualiseurs
bar_system = AnalyticsSystem(processor, bar_visualizer)
line_system = AnalyticsSystem(LegacyDataAnalyzerAdapter(), line_visualizer)
# Exécuter les analyses
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']}")
# Obtenir les résumés
print("Bar system summary:")
print(bar_system.get_analysis_summary())
print("Line system summary:")
print(line_system.get_analysis_summary())Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.
Toutes les leçons de Programmation orientée objet
1Les fondamentaux de la POO
Fichiers externesIntroduction à la POOClasses vs ObjetsLe paramètre selfMéthodesAttributsMéthode constructeur (__init__)Récapitulatif - Calculatrice simple4Héritage
Héritage de baseLa fonction super()Redéfinition de méthodeHéritage multipleOrdre de résolution des méthodesRécapitulatif - Hiérarchie des employés7Méthodes spéciales
Introduction aux méthodes magiquesSurcharge d'opérateursMéthodes magiques de conteneurRécapitulatif - Liste personnalisée10Patrons de conception, Partie 1
Introduction aux patrons de conceptionPatron SingletonPatron FactoryPatron ObserverPatron Strategy2Décorateurs
Introduction aux décorateursDécorateur de propriétéDécorateur de méthode statiqueDécorateur de méthode de classe5Polymorphisme
Retour sur la redéfinition de méthodeDuck TypingClasses et méthodes abstraitesConception d'interfacesRécapitulatif - Calculateur de formes8Concepts avancés de la POO
Composition vs HéritageMixinsMéthodes statiques et de classeDécorateurs de classeGestionnaires de contexte11Patrons de conception, Partie 2
Patron CommandePatron AdaptateurPatron DécorateurPatron Méthode TemplatePatron ÉtatPatron Composite3Propriétés de classe
Variables d'instance vs variables de classeDécorateurs de propriétéAttributs privésRécapitulatif - Gestionnaire de compte bancaireEntraînez-vous par vous-même : Compilateur Python en ligne