Проектирование интерфейсов
Часть раздела Объектно-ориентированное программирование путешествия по Python на Coddy. Урок 26 из 64.
Интерфейс задаёт контракт, которому должны следовать классы. В Python мы создаём интерфейсы с помощью абстрактных базовых классов, в которых все методы являются абстрактными.
Импортируй модуль abc:
from abc import ABC, abstractmethodСоздайте интерфейс, содержащий только абстрактные методы:
class Drawable(ABC):
@abstractmethod
def draw(self):
pass
@abstractmethod
def resize(self, width, height):
passВсе методы в интерфейсе должны быть абстрактными: они определяют, что должны делать реализующие интерфейс классы, а не как это делать.
Реализуйте интерфейс в конкретном классе:
class Circle(Drawable):
def __init__(self, radius):
self.radius = radius
def draw(self):
return "Drawing a circle"
def resize(self, width, height):
self.radius = min(width, height) / 2
return f"Resized circle to radius {self.radius}"Создай ещё один класс, реализующий тот же интерфейс:
class Rectangle(Drawable):
def __init__(self, width, height):
self.width = width
self.height = height
def draw(self):
return "Drawing a rectangle"
def resize(self, width, height):
self.width = width
self.height = height
return f"Resized rectangle to {width}x{height}"Используйте интерфейс полиморфно:
shapes = [Circle(5), Rectangle(3, 4)]
for shape in shapes:
print(shape.draw())
print(shape.resize(10, 8))Вывод:
Drawing a circle
Resized circle to radius 4.0
Drawing a rectangle
Resized rectangle to 10x8Интерфейсы также можно использовать в качестве подсказок типов:
def render_shape(drawable: Drawable):
return drawable.draw()
circle = Circle(3)
print(render_shape(circle))Вывод:
Drawing a circleКлючевой момент: интерфейсы определяют, что должны делать классы, а не то, как они это делают. Используй абстрактные базовые классы только с абстрактными методами, чтобы создавать чёткие контракты, которым должны следовать реализующие их классы. Это обеспечивает единообразное поведение в разных реализациях.
Задание
СреднеВ этом задании ты реализуешь систему медиаплеера с интерфейсами.
Тебе нужно отредактировать следующие файлы:
playable.py— реализуй абстрактные базовые классы (интерфейсы)song.py— реализуй класс Songvideo.py— реализуй класс Videomediaplayer.py— реализуй класс MediaPlayer
В каждом файле есть подробные комментарии TODO, которые помогут тебе выполнить задание. Следуй этим комментариям, чтобы создать полноценную систему медиаплеера с правильным наследованием и реализацией интерфейсов.
Попробуйте сами
from song import Song
from video import Video
from mediaplayer import MediaPlayer
from playable import Playable, MediaInfo
# Обработчик всех тестовых случаев
test_case = input()
if test_case == "default_test":
# Стандартный тестовый случай из исходной задачи
song = Song("Bohemian Rhapsody", "Queen", 355)
video = Video("Python Tutorial", "1080p", 1800)
player = MediaPlayer()
# Проверка с песней
player.set_media(song)
print(player.current_media.get_info())
print(player.play())
print(player.pause())
print(player.stop())
print() # Пустая строка для удобства чтения
# Проверка с видео
player.set_media(video)
print(player.current_media.get_info())
print(player.play())
print(player.pause())
print(player.stop())
elif test_case == "empty_player":
# Проверка медиаплеера без выбранного медиафайла
player = MediaPlayer()
print(player.play()) # Должно вывести "No media set"
print(player.pause()) # Должно вывести "No media set"
print(player.stop()) # Должно вывести "No media set"
elif test_case == "time_formatting":
# Проверка форматирования времени в методе get_info()
song1 = Song("Short Song", "Artist A", 65) # 1:05
song2 = Song("Long Song", "Artist B", 3661) # 61:01
video1 = Video("Hour Video", "720p", 3600) # 60:00
print(song1.get_info())
print(song2.get_info())
print(video1.get_info())
elif test_case == "interface_compliance":
# Проверка того, что Song и Video правильно реализуют интерфейсы
song = Song("Test Song", "Test Artist", 180)
video = Video("Test Video", "480p", 240)
# Проверка реализации интерфейсов
print(f"Song implements Playable: {isinstance(song, Playable)}")
print(f"Song implements MediaInfo: {isinstance(song, MediaInfo)}")
print(f"Video implements Playable: {isinstance(video, Playable)}")
print(f"Video implements MediaInfo: {isinstance(video, MediaInfo)}")
# Проверка всех методов интерфейсов
print(song.play())
print(song.pause())
print(song.stop())
print(song.get_title())
print(song.get_duration())
print(song.get_info())
print(video.play())
print(video.pause())
print(video.stop())
print(video.get_title())
print(video.get_duration())
print(video.get_info())
elif test_case == "polymorphism":
# Проверка полиморфного поведения на списке медиафайлов разных типов
media_list = [
Song("Song 1", "Artist 1", 180),
Video("Video 1", "720p", 300),
Song("Song 2", "Artist 2", 240),
Video("Video 2", "1080p", 420)
]
player = MediaPlayer()
for media in media_list:
player.set_media(media)
print(f"Media: {media.get_title()}")
print(f"Info: {media.get_info()}")
print(f"Play: {player.play()}")
print()
elif test_case == "edge_cases":
# Проверить крайние случаи
empty_song = Song("", "", 0)
edge_video = Video("A" * 100, "", -10) # Очень длинное название, пустое разрешение, отрицательная длительность
print(f"Empty song info: {empty_song.get_info()}")
print(f"Edge video info: {edge_video.get_info()}")
player = MediaPlayer()
player.set_media(empty_song)
print(player.play())
player.set_media(edge_video)
print(player.play())
elif test_case == "stress_test":
# Реализовать стресс-тест с большим количеством медиаобъектов
songs = [Song(f"Song {i}", f"Artist {i}", i * 30) for i in range(1, 101)]
videos = [Video(f"Video {i}", f"{i*10}p", i * 60) for i in range(1, 101)]
player = MediaPlayer()
# Проверить все песни
for i, song in enumerate(songs):
player.set_media(song)
if i % 10 == 0: # Выводить только каждый 10-й результат, чтобы не было слишком много вывода
print(f"Playing song {i+1}: {player.play()}")
# Проверить все видео
for i, video in enumerate(videos):
player.set_media(video)
if i % 10 == 0: # Выводить только каждый 10-й результат, чтобы не было слишком много вывода
print(f"Playing video {i+1}: {player.play()}")
print("Stress test completed successfully!")В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП
Введение в ООПВнешние файлыКлассы и объектыПараметр selfМетодыАтрибутыМетод-конструктор (__init__)Повторение — простой калькулятор4Наследование
Основы наследованияФункция super()Переопределение методовМножественное наследованиеПорядок разрешения методовПовторение: иерархия сотрудников7Специальные методы
Введение в магические методыПерегрузка операторовМагические методы контейнеровПовторение — пользовательский список10Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн «Одиночка»Паттерн «Фабрика»Паттерн «Наблюдатель»Паттерн «Стратегия»2Декораторы
Введение в декораторыДекоратор свойстваДекоратор статического методаДекоратор метода класса5Полиморфизм
Переопределение методов: повторениеУтиная типизацияАбстрактные классы и методыПроектирование интерфейсовПовторение — калькулятор фигур8Продвинутые концепции ООП
Композиция и наследованиеМиксиныСтатические методы и методы классаДекораторы классовМенеджеры контекста11Паттерны проектирования. Часть 2
Паттерн «Команда»Паттерн «Адаптер»Паттерн «Декоратор»Паттерн «Шаблонный метод»Паттерн «Состояние»Паттерн «Компоновщик»3Свойства классов
Переменные экземпляра и классаДекораторы свойствПриватные атрибутыПовторение — менеджер банковского счёта6Инкапсуляция
Публичные, защищённые и приватные членыМодификаторы доступаСокрытие информацииПродвинутые декораторы свойствПовторение — система учёта студентов12Проект: Система управления библиотекой
Обзор проектаКлассы книг и пользователейСистема выдачи книгФункция поискаИнтерфейс администратораТестирование и интеграцияПотренируйтесь самостоятельно: Онлайн-компилятор Python