Menu
Coddy logo textTech

Проектирование интерфейсов

Часть раздела Object Oriented Programming путешествия по 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

Ключевой момент: Интерфейсы определяют, что классы должны делать, а не то, как они это делают. Используйте абстрактные базовые классы, содержащие только абстрактные методы, для создания четких контрактов, которым должны следовать реализующие их классы. Это обеспечивает согласованное поведение в различных реализациях.

challenge icon

Задание

Средне

В этом испытании вы реализуете систему медиаплеера с использованием интерфейсов.

Вам необходимо отредактировать следующие файлы:

  • playable.py — Реализуйте абстрактные базовые классы (интерфейсы)
  • song.py — Реализуйте класс Song
  • video.py — Реализуйте класс Video
  • mediaplayer.py — Реализуйте класс MediaPlayer

Каждый файл содержит подробные комментарии TODO, которые помогут вам в реализации. Следуйте этим комментариям, чтобы создать полноценную систему медиаплеера с правильным наследованием и реализацией интерфейсов.

Шпаргалка

Создавайте интерфейсы, используя абстрактные базовые классы с помощью модуля 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}"

Используйте интерфейсы полиморфно:

shapes = [Circle(5), Rectangle(3, 4)]

for shape in shapes:
    print(shape.draw())
    print(shape.resize(10, 8))

Используйте интерфейсы в качестве подсказок типов:

def render_shape(drawable: Drawable):
    return drawable.draw()

Интерфейсы определяют, что должны делать классы, а не то, как они это делают. Все методы в интерфейсе должны быть абстрактными, чтобы создавать четкие контракты для реализующих классов.

Попробуйте сами

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!")
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Object Oriented Programming