Паттерн Адаптер
Часть раздела Object Oriented Programming путешествия по Python на Coddy — урок 50 из 64.
Паттерн Адаптер позволяет объектам с несовместимыми интерфейсами работать вместе. Он выступает в роли моста, оборачивая существующий класс новым интерфейсом, который ожидают клиенты.
Вот две системы с несовместимыми интерфейсами:
class OldPrinter:
def old_print(self, text):
return f"OLD: {text}"
class NewPrinter:
def print(self, text):
return f"NEW: {text}"Старый принтер использует old_print(), в то время как новый использует print().
Создайте адаптер, чтобы старый принтер работал с новым интерфейсом:
class PrinterAdapter:
def __init__(self, old_printer):
self.old_printer = old_printer
def print(self, text):
# Адаптация старого интерфейса к новому
return self.old_printer.old_print(text)Адаптер оборачивает старый принтер и предоставляет ожидаемый интерфейс.
Используйте оба принтера с одним и тем же клиентским кодом:
def print_document(printer, text):
return printer.print(text) # Ожидает метод print()
# Использование нового принтера напрямую
new_printer = NewPrinter()
print(print_document(new_printer, "Hello"))
# Использование старого принтера через адаптер
old_printer = OldPrinter()
adapter = PrinterAdapter(old_printer)
print(print_document(adapter, "Hello"))Создайте еще один пример с медиаплеерами:
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"))Вывод:
NEW: Hello
OLD: Hello
Playing MP3: song.mp3
Playing MP4: video.mp4Ключевой момент: Паттерн Адаптер позволяет несовместимым интерфейсам работать вместе путем оборачивания существующего класса новым интерфейсом. Адаптер преобразует вызовы из ожидаемого интерфейса в фактический интерфейс обернутого объекта. Это полезно для интеграции устаревшего кода или сторонних библиотек без изменения существующего кода.
Задание
СреднеВ этом испытании вы реализуете паттерн проектирования Адаптер (Adapter) для интеграции устаревших систем с современной архитектурой приложения. Паттерн Адаптер позволяет объектам с несовместимыми интерфейсами работать вместе, создавая обертку (адаптер), которая преобразует один интерфейс в другой.
У вашей компании есть устаревшая система анализа и визуализации данных, которую необходимо интегрировать с новой современной аналитической платформой. Устаревшие компоненты имеют интерфейсы, несовместимые с новой системой. Вместо того чтобы переписывать устаревший код (что было бы рискованно и дорого), вам поручено создать адаптеры, чтобы эти компоненты могли работать с новой системой.
Вам необходимо:
- Изучить современные интерфейсы, определенные в
interfaces.py(этот файл нельзя изменять) - Изучить устаревшие компоненты в
legacy_system.py(этот файл нельзя изменять) - Реализовать классы адаптеров в
adapters.py, которые сделают устаревшие компоненты совместимыми с современными интерфейсами - Создать современную систему в
modern_system.py, которая использует эти интерфейсы
Шпаргалка
Паттерн «Адаптер» позволяет объектам с несовместимыми интерфейсами работать вместе, оборачивая существующий класс новым интерфейсом, который ожидают клиенты.
Базовая структура адаптера:
class PrinterAdapter:
def __init__(self, old_printer):
self.old_printer = old_printer
def print(self, text):
# Адаптация старого интерфейса к новому интерфейсу
return self.old_printer.old_print(text)Пример с несовместимыми интерфейсами принтеров:
class OldPrinter:
def old_print(self, text):
return f"OLD: {text}"
class NewPrinter:
def print(self, text):
return f"NEW: {text}"
# Клиентский код ожидает метод print()
def print_document(printer, text):
return printer.print(text)
# Использование старого принтера через адаптер
old_printer = OldPrinter()
adapter = PrinterAdapter(old_printer)
print(print_document(adapter, "Hello")) # OLD: HelloПример адаптера медиаплеера:
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)Адаптер транслирует вызовы из ожидаемого интерфейса в фактический интерфейс обернутого объекта, обеспечивая интеграцию устаревшего кода без его изменения.
Попробуйте сами
# Импорт всех необходимых классов
from adapters import LegacyDataAnalyzerAdapter, LegacyChartGeneratorAdapter
from modern_system import AnalyticsSystem
# Обработчик комплексных тестовых случаев
test_case = input()
if test_case == "basic_adapter_test":
# Тестирование базовой функциональности адаптера
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":
# Тестирование адаптера визуализатора
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":
# Тестирование полной аналитической системы
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":
# Тестирование ошибок валидации
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":
# Тестирование с пустыми данными
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":
# Тестирование различных типов диаграмм
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":
# Тестирование обработки расширений файлов
visualizer = LegacyChartGeneratorAdapter()
data = [1, 2, 3, 4]
visualizer.visualize(data)
# Тестирование различных форматов имен файлов
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":
# Тестирование сводки анализа
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":
# Тестирование со смешанными числовыми данными
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":
# Тестирование с некорректными числовыми данными
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":
# Тестирование сводки без результатов
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":
# Тестирование с большим набором данных
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":
# Тестирование полного рабочего процесса с несколькими операциями
processor = LegacyDataAnalyzerAdapter()
bar_visualizer = LegacyChartGeneratorAdapter("bar")
line_visualizer = LegacyChartGeneratorAdapter("line")
data = [12, 18, 25, 32, 28, 35, 42]
# Создание аналитических систем с различными визуализаторами
bar_system = AnalyticsSystem(processor, bar_visualizer)
line_system = AnalyticsSystem(LegacyDataAnalyzerAdapter(), line_visualizer)
# Запуск анализов
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']}")
# Получение сводок
print("Bar system summary:")
print(bar_system.get_analysis_summary())
print("Line system summary:")
print(line_system.get_analysis_summary())В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Object Oriented Programming
1Основы ООП
Внешние файлыВведение в ООПКлассы и объектыПараметр selfМетодыАтрибутыМетод-конструктор (__init__)Повторение — Простой калькулятор4Наследование
Базовое наследованиеФункция super()Переопределение методовМножественное наследованиеПорядок разрешения методовИтоги — Иерархия сотрудников7Специальные методы
Введение в магические методыПерегрузка операторовМагические методы контейнеровПовторение — Пользовательский список10Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн SingletonПаттерн FactoryПаттерн ObserverПаттерн Strategy5Полиморфизм
Снова о переопределении методовУтиная типизацияАбстрактные классы и методыПроектирование интерфейсовИтоги — Калькулятор фигур8Продвинутые концепции ООП
Композиция против наследованияМиксиныСтатические методы и методы классаДекораторы классовМенеджеры контекста11Паттерны проектирования. Часть 2
Паттерн КомандаПаттерн АдаптерПаттерн ДекораторПаттерн Шаблонный методПаттерн СостояниеПаттерн Компоновщик3Свойства классов
Переменные экземпляра и классаДекораторы свойствПриватные атрибутыИтоги — Менеджер банковского счета6Инкапсуляция
Public, Protected, Private члены классаМодификаторы доступаСокрытие данныхДекораторы property: продвинутый уровеньИтоги: Система учета студентов12Проект: Управление библиотекой
Обзор проектаКлассы Book и User