Menu
Coddy logo textTech

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.

Importieren Sie das abc-Modul:

from abc import ABC, abstractmethod

Erstelle eine Schnittstelle, die nur abstrakte Methoden enthält:

class Drawable(ABC):
    @abstractmethod
    def draw(self):
        pass
    
    @abstractmethod
    def resize(self, width, height):
        pass

Alle 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}"

Erstellen Sie 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 10x8

Du kannst Schnittstellen auch als Typhinweise verwenden:

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

circle = Circle(3)
print(render_shape(circle))

Ausgabe:

Drawing a circle

Kernpunkt: Interfaces definieren, was Klassen tun müssen, nicht wie sie es tun. Verwenden Sie abstrakte Basisklassen mit ausschließlich abstrakten Methoden, um klare Verträge zu erstellen, denen implementierende Klassen folgen müssen. Dies gewährleistet ein konsistentes Verhalten über verschiedene Implementierungen hinweg.

challenge icon

Aufgabe

Mittel

In 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 ausführliche 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 der ursprünglichen Problemstellung
    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 bessere 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":
    # Testet den Media Player ohne gesetztes Medium
    player = MediaPlayer()
    print(player.play())  # Sollte "No media set" ausgeben
    print(player.pause())  # Sollte "No media set" ausgeben
    print(player.stop())  # Sollte "No media set" ausgeben

elif test_case == "time_formatting":
    # Testet 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":
    # Testet, ob Song und Video die Interfaces korrekt implementieren
    song = Song("Test Song", "Test Artist", 180)
    video = Video("Test Video", "480p", 240)
    
    # Überprüfung der 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)}")
    
    # Testet 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":
    # Testet polymorphes 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":
    # Testet 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":
    # Implementiert einen Belastungstest 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()
    
    # Test mit allen Songs
    for i, song in enumerate(songs):
        player.set_media(song)
        if i % 10 == 0:  # Nur jeden 10. ausgeben, um zu viel Output zu vermeiden
            print(f"Playing song {i+1}: {player.play()}")
    
    # Test mit allen Videos
    for i, video in enumerate(videos):
        player.set_media(video)
        if i % 10 == 0:  # Nur jeden 10. ausgeben, um zu viel Output zu vermeiden
            print(f"Playing video {i+1}: {player.play()}")
    
    print("Stress test completed successfully!")
quiz iconTeste dich selbst

Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.

Alle Lektionen in Objektorientierte Programmierung

Übe selbstständig: Online-Python-Compiler