Schnittstellendesign
Teil des Abschnitts Objektorientierte Programmierung der Python-Journey von Coddy. Lektion 26 von 64.
Eine Schnittstelle definiert einen Vertrag, dem Klassen folgen müssen. In Python erstellen wir Schnittstellen mithilfe abstrakter Basisklassen, bei denen alle Methoden abstrakt sind.
Importiere das abc-Modul:
from abc import ABC, abstractmethodErstelle eine Schnittstelle, die nur abstrakte Methoden enthält:
class Drawable(ABC):
@abstractmethod
def draw(self):
pass
@abstractmethod
def resize(self, width, height):
passAlle Methoden in einem Interface sollten abstrakt sein – sie legen fest, was implementierende Klassen tun müssen, nicht wie sie es tun sollen.
Implementiere die Schnittstelle in einer konkreten Klasse:
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}"Erstelle eine weitere Klasse, die dieselbe Schnittstelle implementiert:
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}"Verwende die Schnittstelle polymorph:
shapes = [Circle(5), Rectangle(3, 4)]
for shape in shapes:
print(shape.draw())
print(shape.resize(10, 8))Ausgabe:
Drawing a circle
Resized circle to radius 4.0
Drawing a rectangle
Resized rectangle to 10x8Du kannst Schnittstellen auch als Typhinweise verwenden:
def render_shape(drawable: Drawable):
return drawable.draw()
circle = Circle(3)
print(render_shape(circle))Ausgabe:
Drawing a circleWichtiger Punkt: Schnittstellen definieren, was Klassen tun müssen, nicht wie sie es tun. Verwende abstrakte Basisklassen mit ausschließlich abstrakten Methoden, um klare Verträge zu erstellen, die implementierende Klassen einhalten müssen. Dadurch wird ein konsistentes Verhalten über verschiedene Implementierungen hinweg sichergestellt.
Aufgabe
MittelIn dieser Herausforderung implementierst du ein Mediaplayer-System mit Schnittstellen.
Du musst die folgenden Dateien bearbeiten:
playable.py– Implementiere die abstrakten Basisklassen (Schnittstellen).song.py– Implementiere die Klasse Song.video.py– Implementiere die Klasse Video.mediaplayer.py– Implementiere die Klasse MediaPlayer.
Jede Datei enthält detaillierte TODO-Kommentare, die dich bei der Implementierung anleiten. Befolge diese Kommentare, um ein vollständiges Mediaplayer-System mit korrekter Vererbung und Schnittstellenimplementierung zu erstellen.
Probier es selbst
from song import Song
from video import Video
from mediaplayer import MediaPlayer
from playable import Playable, MediaInfo
# Umfassender Testfall-Handler
test_case = input()
if test_case == "default_test":
# Standard-Testfall aus dem ursprünglichen Problem
song = Song("Bohemian Rhapsody", "Queen", 355)
video = Video("Python Tutorial", "1080p", 1800)
player = MediaPlayer()
# Test mit Song
player.set_media(song)
print(player.current_media.get_info())
print(player.play())
print(player.pause())
print(player.stop())
print() # Leerzeile für die Lesbarkeit
# Test mit Video
player.set_media(video)
print(player.current_media.get_info())
print(player.play())
print(player.pause())
print(player.stop())
elif test_case == "empty_player":
# Teste den Media-Player ohne gesetzte Medien
player = MediaPlayer()
print(player.play()) # Should print "No media set"
print(player.pause()) # Should print "No media set"
print(player.stop()) # Should print "No media set"
elif test_case == "time_formatting":
# Teste die Zeitformatierung in der get_info()-Methode
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":
# Teste, dass Song und Video die Interfaces ordnungsgemäß implementieren
song = Song("Test Song", "Test Artist", 180)
video = Video("Test Video", "480p", 240)
# Überprüfe die Interface-Implementierung
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)}")
# Teste alle Interface-Methoden
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":
# Teste polymorphisches Verhalten mit einer Liste verschiedener Medientypen
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":
# Teste Grenzfälle
empty_song = Song("", "", 0)
edge_video = Video("A" * 100, "", -10) # Sehr langer Titel, leere Auflösung, negative Dauer
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":
# Implementiere einen Stresstest mit vielen Medienobjekten
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()
# Teste mit allen Songs
for i, song in enumerate(songs):
player.set_media(song)
if i % 10 == 0: # Nur jedes 10. ausgeben, um zu viel Ausgabe zu vermeiden
print(f"Playing song {i+1}: {player.play()}")
# Teste mit allen Videos
for i, video in enumerate(videos):
player.set_media(video)
if i % 10 == 0: # Nur jedes 10. ausgeben, um zu viel Ausgabe zu vermeiden
print(f"Playing video {i+1}: {player.play()}")
print("Stress test completed successfully!")Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Objektorientierte Programmierung
1Grundlagen der OOP
Externe DateienEinführung in die OOPKlassen vs. ObjekteDer self-ParameterMethodenAttributeKonstruktormethode (__init__)Zusammenfassung – Einfacher Taschenrechner4Vererbung
Grundlegende VererbungDie super()-FunktionMethodenüberschreibungMehrfachvererbungMethodenauflösungsreihenfolgeWiederholung – Mitarbeiterhierarchie7Spezielle Methoden
Einführung in magische MethodenOperatorüberladungMagische Methoden für ContainerRückblick – Benutzerdefinierte Liste10Entwurfsmuster Teil 1
Einführung in EntwurfsmusterSingleton-MusterFactory-MusterObserver-MusterStrategy-Muster13Abschließende Herausforderungen
E-Learning-PlattformBankensystemEntwicklung von SpielfigurenFahrzeugvermietungsservice2Dekoratoren
Einführung in DekoratorenProperty-DekoratorDekorator für statische MethodenDekorator für Klassenmethoden5Polymorphie
Methodenüberschreibung erneut betrachtetDuck-TypingAbstrakte Klassen und MethodenSchnittstellendesignRückblick – Formrechner8Fortgeschrittene OOP-Konzepte
Komposition vs. VererbungMixinsStatische Methoden und KlassenmethodenKlassen-DekoratorenKontextmanager3Klassenattribute
Instanz- vs. KlassenvariablenProperty-DecoratorsPrivate AttributeWiederholung – Bankkontoverwaltung6Kapselung
Öffentliche, geschützte, private MemberZugriffsmodifikatorenInformationsverbergungProperty-Decorators – FortgeschrittenWiederholung – System zur Verwaltung von Studentendaten12Projekt: Bibliotheksverwaltung
ProjektübersichtBuch- und BenutzerklassenAusleihsystemSuchfunktionAdministrationsoberflächeTests und IntegrationÜbe selbstständig: Online-Python-Compiler