Menu
Coddy logo textTech

Conception d'interfaces

Fait partie de la section Object Oriented Programming du Journey Python de Coddy — leçon 26 sur 64.

Une interface définit un contrat que les classes doivent suivre. En Python, nous créons des interfaces en utilisant des classes de base abstraites où toutes les méthodes sont abstraites.

Importez le module abc :

from abc import ABC, abstractmethod

Créez une interface avec uniquement des méthodes abstraites :

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

Toutes les méthodes d'une interface doivent être abstraites - elles définissent ce que les classes d'implémentation doivent faire, et non comment le faire.

Implémentez l'interface dans une classe concrète :

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

Créez une autre classe qui implémente la même interface :

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

Utilisez l'interface de manière polymorphe :

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

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

Sortie :

Drawing a circle
Resized circle to radius 4.0
Drawing a rectangle
Resized rectangle to 10x8

Vous pouvez également utiliser des interfaces comme indices de type :

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

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

Sortie :

Drawing a circle

Point clé : Les interfaces définissent ce que les classes doivent faire, pas comment elles le font. Utilisez des classes de base abstraites avec uniquement des méthodes abstraites pour créer des contrats clairs que les classes d'implémentation doivent suivre. Cela garantit un comportement cohérent à travers différentes implémentations.

challenge icon

Défi

Moyen

Dans ce défi, vous allez implémenter un système de lecteur multimédia avec des interfaces.

Vous devez modifier les fichiers suivants :

  • playable.py - Implémentez les classes de base abstraites (interfaces)
  • song.py - Implémentez la classe Song
  • video.py - Implémentez la classe Video
  • mediaplayer.py - Implémentez la classe MediaPlayer

Chaque fichier contient des commentaires TODO détaillés pour guider votre implémentation. Suivez ces commentaires pour créer un système de lecteur multimédia complet avec un héritage et une implémentation d'interface appropriés.

Aide-mémoire

Créez des interfaces en utilisant des classes de base abstraites avec le module abc :

from abc import ABC, abstractmethod

Définissez une interface avec uniquement des méthodes abstraites :

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

Implémentez l'interface dans des classes concrètes :

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

Utilisez les interfaces de manière polymorphe :

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

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

Utilisez les interfaces comme indices de type (type hints) :

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

Les interfaces définissent ce que les classes doivent faire, et non comment elles le font. Toutes les méthodes d'une interface doivent être abstraites pour créer des contrats clairs pour les classes d'implémentation.

Essayez vous-même

from song import Song
from video import Video
from mediaplayer import MediaPlayer
from playable import Playable, MediaInfo

# Gestionnaire de cas de test complet
test_case = input()

if test_case == "default_test":
    # Cas de test par défaut du problème original
    song = Song("Bohemian Rhapsody", "Queen", 355)
    video = Video("Python Tutorial", "1080p", 1800)
    player = MediaPlayer()

    # Test avec une chanson
    player.set_media(song)
    print(player.current_media.get_info())
    print(player.play())
    print(player.pause())
    print(player.stop())
    print()  # Ligne vide pour la lisibilité

    # Test avec une vidéo
    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 du lecteur multimédia sans aucun média défini
    player = MediaPlayer()
    print(player.play())  # Devrait afficher "No media set"
    print(player.pause())  # Devrait afficher "No media set"
    print(player.stop())  # Devrait afficher "No media set"

elif test_case == "time_formatting":
    # Test du formatage du temps dans la méthode 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 que Song et Video implémentent correctement les interfaces
    song = Song("Test Song", "Test Artist", 180)
    video = Video("Test Video", "480p", 240)
    
    # Vérification de l'implémentation des interfaces
    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 de toutes les méthodes d'interface
    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 du comportement polymorphe avec une liste de différents types de médias
    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":
    # Test des cas limites
    empty_song = Song("", "", 0)
    edge_video = Video("A" * 100, "", -10)  # Titre très long, résolution vide, durée négative
    
    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":
    # Implémentation d'un test de charge avec de nombreux objets multimédias
    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 avec toutes les chansons
    for i, song in enumerate(songs):
        player.set_media(song)
        if i % 10 == 0:  # Affiche seulement un élément sur 10 pour éviter trop de sorties
            print(f"Playing song {i+1}: {player.play()}")
    
    # Test avec toutes les vidéos
    for i, video in enumerate(videos):
        player.set_media(video)
        if i % 10 == 0:  # Affiche seulement un élément sur 10 pour éviter trop de sorties
            print(f"Playing video {i+1}: {player.play()}")
    
    print("Stress test completed successfully!")
quiz iconTestez-vous

Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.

Toutes les leçons de Object Oriented Programming