Wzorzec Adapter
Część sekcji Programowanie obiektowe ścieżki Python w Coddy. Lekcja 50 z 64.
Wzorzec Adapter umożliwia współpracę obiektom o niezgodnych interfejsach. Działa jak most, opakowując istniejącą klasę w nowy interfejs, którego oczekują klienci.
Oto dwa systemy z niekompatybilnymi interfejsami:
class OldPrinter:
def old_print(self, text):
return f"OLD: {text}"
class NewPrinter:
def print(self, text):
return f"NEW: {text}"Stara drukarka używa old_print(), a nowa print().
Utwórz adapter, aby stara drukarka działała z nowym interfejsem:
class PrinterAdapter:
def __init__(self, old_printer):
self.old_printer = old_printer
def print(self, text):
# Dostosuj stary interfejs do nowego interfejsu
return self.old_printer.old_print(text)Adapter opakowuje starą drukarkę i udostępnia oczekiwany interfejs.
Użyj obu drukarek z tym samym kodem klienta:
def print_document(printer, text):
return printer.print(text) # Oczekuje metody print()
# Użyj nowej drukarki bezpośrednio
new_printer = NewPrinter()
print(print_document(new_printer, "Hello"))
# Użyj starej drukarki przez adapter
old_printer = OldPrinter()
adapter = PrinterAdapter(old_printer)
print(print_document(adapter, "Hello"))Utwórz kolejny przykład z odtwarzaczami multimedialnymi:
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"))Wynik:
NEW: Hello
OLD: Hello
Playing MP3: song.mp3
Playing MP4: video.mp4Kluczowa kwestia: Wzorzec Adaptera umożliwia współpracę niekompatybilnych interfejsów, opakowując istniejącą klasę w nowy interfejs. Adapter tłumaczy wywołania z oczekiwanego interfejsu na rzeczywisty interfejs opakowanego obiektu. Jest to przydatne przy integrowaniu starszego kodu lub bibliotek innych firm bez modyfikowania istniejącego kodu.
Wyzwanie
ŚredniW tym wyzwaniu zaimplementujesz wzorzec projektowy Adapter, aby zintegrować starsze systemy z nowoczesną architekturą aplikacji. Wzorzec Adapter pozwala współpracować obiektom z niezgodnymi interfejsami, tworząc opakowanie (adapter), które tłumaczy jeden interfejs na inny.
Twoja firma ma starszy system analizy i wizualizacji danych, który trzeba zintegrować z nową, nowoczesną platformą analityczną. Starsze komponenty mają interfejsy niezgodne z nowym systemem. Zamiast przepisywać starszy kod (co byłoby ryzykowne i kosztowne), masz za zadanie utworzyć adaptery, które umożliwią współpracę tych komponentów z nowym systemem.
Musisz:
- Zapoznać się z nowoczesnymi interfejsami zdefiniowanymi w
interfaces.py(tego pliku nie można modyfikować) - Przeanalizować starsze komponenty w
legacy_system.py(tego pliku nie można modyfikować) - Zaimplementować klasy adapterów w
adapters.py, które dostosują starsze komponenty do nowoczesnych interfejsów - Utworzyć nowoczesny system w
modern_system.py, który korzysta z tych interfejsów
Spróbuj swoich sił
# Importuj wszystkie niezbędne klasy
from adapters import LegacyDataAnalyzerAdapter, LegacyChartGeneratorAdapter
from modern_system import AnalyticsSystem
# Kompleksowa obsługa przypadków testowych
test_case = input()
if test_case == "basic_adapter_test":
# Przetestuj podstawowe działanie adaptera
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":
# Przetestuj adapter wizualizacji
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":
# Przetestuj cały system analityczny
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":
# Przetestuj błędy walidacji
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":
# Przetestuj działanie z pustymi danymi
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":
# Przetestuj różne typy wykresów
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":
# Przetestuj obsługę rozszerzeń plików
visualizer = LegacyChartGeneratorAdapter()
data = [1, 2, 3, 4]
visualizer.visualize(data)
# Przetestuj różne formaty nazw plików
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":
# Przetestuj podsumowanie analizy
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":
# Przetestuj działanie z danymi zawierającymi różne typy liczb
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":
# Przetestuj nieprawidłowe dane liczbowe
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":
# Przetestuj podsumowanie bez wyników
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":
# Przetestuj na większym zbiorze danych
processor = LegacyDataAnalyzerAdapter()
data = list(range(1, 101)) # Od 1 do 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":
# Przetestuj pełny przebieg pracy z wieloma operacjami
processor = LegacyDataAnalyzerAdapter()
bar_visualizer = LegacyChartGeneratorAdapter("bar")
line_visualizer = LegacyChartGeneratorAdapter("line")
data = [12, 18, 25, 32, 28, 35, 42]
# Utwórz systemy analityczne z różnymi wizualizatorami
bar_system = AnalyticsSystem(processor, bar_visualizer)
line_system = AnalyticsSystem(LegacyDataAnalyzerAdapter(), line_visualizer)
# Uruchom analizy
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']}")
# Pobierz podsumowania
print("Bar system summary:")
print(bar_system.get_analysis_summary())
print("Line system summary:")
print(line_system.get_analysis_summary())Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.
Wszystkie lekcje w sekcji Programowanie obiektowe
1Podstawy programowania obiektowego
Pliki zewnętrzneWprowadzenie do programowania obiektowegoKlasy a obiektyParametr selfMetodyAtrybutyMetoda konstruktora (__init__)Powtórzenie – prosty kalkulator4Dziedziczenie
Podstawy dziedziczeniaFunkcja super()Nadpisywanie metodDziedziczenie wielokrotneKolejność rozwiązywania metodPodsumowanie – hierarchia pracowników7Metody specjalne
Wprowadzenie do metod magicznychPrzeciążanie operatorówMagiczne metody kontenerówPowtórka – własna lista10Wzorce projektowe, część 1
Wprowadzenie do wzorców projektowychWzorzec SingletonWzorzec FactoryWzorzec ObserverWzorzec Strategy2Dekoratory
Wprowadzenie do dekoratorówDekorator propertyDekorator metody statycznejDekorator metody klasowej5Polimorfizm
Ponowne omówienie nadpisywania metodTypowanie kaczeKlasy i metody abstrakcyjneProjektowanie interfejsówPodsumowanie — kalkulator kształtów8Zaawansowane koncepcje programowania obiektowego
Kompozycja a dziedziczenieMiksyMetody statyczne i klasoweDekoratory klasMenedżery kontekstu11Wzorce projektowe, część 2
Wzorzec PolecenieWzorzec AdapterWzorzec DekoratorWzorzec Metoda szablonowaWzorzec StanWzorzec Kompozyt3Właściwości klas
Zmienne instancji a zmienne klasoweDekoratory właściwościAtrybuty prywatnePowtórzenie — menedżer kont bankowych6Hermetyzacja
Public, Protected, Private — pamięćModyfikatory dostępuUkrywanie informacjiDekoratory właściwości — zaawansowanePodsumowanie — system ewidencji uczniów12Projekt: System zarządzania biblioteką
Przegląd projektuKlasy Book i UserSystem wypożyczeńFunkcja wyszukiwaniaInterfejs administratoraTestowanie i integracjaPoćwicz samodzielnie: Kompilator Python online