Паттерн «Адаптер»
Часть раздела Объектно-ориентированное программирование путешествия по 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Ключевой момент: шаблон «Адаптер» позволяет несовместимым интерфейсам работать вместе, оборачивая существующий класс новым интерфейсом. Адаптер преобразует вызовы ожидаемого интерфейса в вызовы фактического интерфейса обёрнутого объекта. Это полезно для интеграции устаревшего кода или сторонних библиотек без изменения существующего кода.
Задание
СреднеВ этом задании вы реализуете структурный шаблон «Адаптер», чтобы интегрировать устаревшие системы в современную архитектуру приложения. Шаблон «Адаптер» позволяет объектам с несовместимыми интерфейсами работать вместе, создавая обёртку (адаптер), которая преобразует один интерфейс в другой.
В вашей компании есть устаревшая система анализа и визуализации данных, которую необходимо интегрировать с новой современной аналитической платформой. Интерфейсы устаревших компонентов несовместимы с новой системой. Вместо того чтобы переписывать устаревший код (что было бы рискованно и дорого), вы получили задачу создать адаптеры, позволяющие этим компонентам работать с новой системой.
Вам нужно:
- Изучить современные интерфейсы, определённые в
interfaces.py(этот файл нельзя изменять) - Изучить устаревшие компоненты в
legacy_system.py(этот файл нельзя изменять) - Реализовать классы-адаптеры в
adapters.py, чтобы обеспечить совместимость устаревших компонентов с современными интерфейсами - Создать современную систему в
modern_system.py, которая использует эти интерфейсы
Попробуйте сами
# Импортировать все необходимые классы
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())В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП
Введение в ООПВнешние файлыКлассы и объектыПараметр selfМетодыАтрибутыМетод-конструктор (__init__)Повторение — простой калькулятор4Наследование
Основы наследованияФункция super()Переопределение методовМножественное наследованиеПорядок разрешения методовПовторение: иерархия сотрудников7Специальные методы
Введение в магические методыПерегрузка операторовМагические методы контейнеровПовторение — пользовательский список10Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн «Одиночка»Паттерн «Фабрика»Паттерн «Наблюдатель»Паттерн «Стратегия»2Декораторы
Введение в декораторыДекоратор свойстваДекоратор статического методаДекоратор метода класса5Полиморфизм
Переопределение методов: повторениеУтиная типизацияАбстрактные классы и методыПроектирование интерфейсовПовторение — калькулятор фигур8Продвинутые концепции ООП
Композиция и наследованиеМиксиныСтатические методы и методы классаДекораторы классовМенеджеры контекста11Паттерны проектирования. Часть 2
Паттерн «Команда»Паттерн «Адаптер»Паттерн «Декоратор»Паттерн «Шаблонный метод»Паттерн «Состояние»Паттерн «Компоновщик»3Свойства классов
Переменные экземпляра и классаДекораторы свойствПриватные атрибутыПовторение — менеджер банковского счёта6Инкапсуляция
Публичные, защищённые и приватные членыМодификаторы доступаСокрытие информацииПродвинутые декораторы свойствПовторение — система учёта студентов12Проект: Система управления библиотекой
Обзор проектаКлассы книг и пользователейСистема выдачи книгФункция поискаИнтерфейс администратораТестирование и интеграцияПотренируйтесь самостоятельно: Онлайн-компилятор Python