Projektowanie interfejsów
Część sekcji Programowanie obiektowe ścieżki Python w Coddy. Lekcja 26 z 64.
Interfejs definiuje kontrakt, którego muszą przestrzegać klasy. W Pythonie tworzymy interfejsy za pomocą abstrakcyjnych klas bazowych, w których wszystkie metody są abstrakcyjne.
Zaimportuj moduł abc:
from abc import ABC, abstractmethodUtwórz interfejs zawierający wyłącznie metody abstrakcyjne:
class Drawable(ABC):
@abstractmethod
def draw(self):
pass
@abstractmethod
def resize(self, width, height):
passWszystkie metody w interfejsie powinny być abstrakcyjne — określają, co muszą robić klasy implementujące interfejs, a nie jak mają to robić.
Zaimplementuj interfejs w konkretnej klasie:
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}"Utwórz kolejną klasę, która implementuje ten sam interfejs:
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}"Użyj interfejsu polimorficznie:
shapes = [Circle(5), Rectangle(3, 4)]
for shape in shapes:
print(shape.draw())
print(shape.resize(10, 8))Wynik:
Drawing a circle
Resized circle to radius 4.0
Drawing a rectangle
Resized rectangle to 10x8Możesz także używać interfejsów jako podpowiedzi typów:
def render_shape(drawable: Drawable):
return drawable.draw()
circle = Circle(3)
print(render_shape(circle))Wynik:
Drawing a circleNajważniejsza kwestia: interfejsy określają, co klasy muszą robić, a nie jak mają to robić. Używaj abstrakcyjnych klas bazowych zawierających wyłącznie metody abstrakcyjne, aby tworzyć jasne kontrakty, których muszą przestrzegać klasy implementujące. Zapewnia to spójne działanie różnych implementacji.
Wyzwanie
ŚredniW tym wyzwaniu zaimplementujesz system odtwarzacza multimediów z interfejsami.
Musisz edytować następujące pliki:
playable.py— zaimplementuj abstrakcyjne klasy bazowe (interfejsy)song.py— zaimplementuj klasę Songvideo.py— zaimplementuj klasę Videomediaplayer.py— zaimplementuj klasę MediaPlayer
Każdy plik zawiera szczegółowe komentarze TODO, które pomogą Ci w implementacji. Postępuj zgodnie z tymi komentarzami, aby utworzyć kompletny system odtwarzacza multimediów z odpowiednim dziedziczeniem i implementacją interfejsów.
Spróbuj swoich sił
from song import Song
from video import Video
from mediaplayer import MediaPlayer
from playable import Playable, MediaInfo
# Kompleksowa obsługa przypadków testowych
test_case = input()
if test_case == "default_test":
# Domyślny przypadek testowy z oryginalnego zadania
song = Song("Bohemian Rhapsody", "Queen", 355)
video = Video("Python Tutorial", "1080p", 1800)
player = MediaPlayer()
# Test z utworem
player.set_media(song)
print(player.current_media.get_info())
print(player.play())
print(player.pause())
print(player.stop())
print() # Pusta linia dla czytelności
# Test z filmem
player.set_media(video)
print(player.current_media.get_info())
print(player.play())
print(player.pause())
print(player.stop())
elif test_case == "empty_player":
# Test odtwarzacza multimediów bez ustawionych mediów
player = MediaPlayer()
print(player.play()) # Powinno wypisać "No media set"
print(player.pause()) # Powinno wypisać "No media set"
print(player.stop()) # Powinno wypisać "No media set"
elif test_case == "time_formatting":
# Test formatowania czasu w metodzie 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":
# Test, czy Song i Video poprawnie implementują interfejsy
song = Song("Test Song", "Test Artist", 180)
video = Video("Test Video", "480p", 240)
# Sprawdzenie implementacji interfejsów
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)}")
# Test wszystkich metod interfejsów
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":
# Test zachowania polimorficznego na liście różnych typów mediów
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":
# Przetestuj przypadki brzegowe
empty_song = Song("", "", 0)
edge_video = Video("A" * 100, "", -10) # Bardzo długi tytuł, pusta rozdzielczość, ujemny czas trwania
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":
# Zaimplementuj test obciążeniowy z wieloma obiektami multimedialnymi
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()
# Przetestuj wszystkie utwory
for i, song in enumerate(songs):
player.set_media(song)
if i % 10 == 0: # Wypisuj tylko co 10. wynik, aby uniknąć zbyt dużej ilości danych wyjściowych
print(f"Playing song {i+1}: {player.play()}")
# Przetestuj wszystkie filmy
for i, video in enumerate(videos):
player.set_media(video)
if i % 10 == 0: # Wypisuj tylko co 10. wynik, aby uniknąć zbyt dużej ilości danych wyjściowych
print(f"Playing video {i+1}: {player.play()}")
print("Stress test completed successfully!")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