Menu
Coddy logo textTech

Design de Interfaces

Parte da seção Object Oriented Programming do Journey de Python da Coddy — lição 26 de 64.

Uma interface define um contrato que as classes devem seguir. Em Python, criamos interfaces usando classes base abstratas onde todos os métodos são abstratos.

Importe o módulo abc:

from abc import ABC, abstractmethod

Crie uma interface apenas com métodos abstratos:

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

Todos os métodos em uma interface devem ser abstratos - eles definem o que as classes implementadoras devem fazer, não como fazê-lo.

Implemente a interface em uma classe concreta:

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

Crie outra classe que implemente a mesma 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}"

Use a interface de forma polimórfica:

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

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

Saída:

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

Você também pode usar interfaces como dicas de tipo (type hints):

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

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

Saída:

Drawing a circle

Ponto Chave: Interfaces definem o que as classes devem fazer, não como elas fazem. Use classes base abstratas com apenas métodos abstratos para criar contratos claros que as classes implementadoras devem seguir. Isso garante um comportamento consistente entre diferentes implementações.

challenge icon

Desafio

Médio

Neste desafio, você implementará um sistema de player de mídia com interfaces.

Você precisa editar os seguintes arquivos:

  • playable.py - Implementar as classes base abstratas (interfaces)
  • song.py - Implementar a classe Song
  • video.py - Implementar a classe Video
  • mediaplayer.py - Implementar a classe MediaPlayer

Cada arquivo contém comentários TODO detalhados para guiar sua implementação. Siga estes comentários para criar um sistema de player de mídia completo com herança e implementação de interface adequadas.

Folha de consulta

Crie interfaces usando classes base abstratas com o módulo abc:

from abc import ABC, abstractmethod

Defina uma interface apenas com métodos abstratos:

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

Implemente a interface em classes concretas:

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

Use interfaces de forma polimórfica:

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

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

Use interfaces como dicas de tipo (type hints):

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

Interfaces definem o que as classes devem fazer, não como elas fazem. Todos os métodos em uma interface devem ser abstratos para criar contratos claros para as classes implementadoras.

Experimente você mesmo

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

# Manipulador abrangente de casos de teste
test_case = input()

if test_case == "default_test":
    # Caso de teste padrão do problema original
    song = Song("Bohemian Rhapsody", "Queen", 355)
    video = Video("Python Tutorial", "1080p", 1800)
    player = MediaPlayer()

    # Teste com música
    player.set_media(song)
    print(player.current_media.get_info())
    print(player.play())
    print(player.pause())
    print(player.stop())
    print()  # Linha vazia para legibilidade

    # Teste com vídeo
    player.set_media(video)
    print(player.current_media.get_info())
    print(player.play())
    print(player.pause())
    print(player.stop())

elif test_case == "empty_player":
    # Testa o reprodutor de mídia sem nenhuma mídia definida
    player = MediaPlayer()
    print(player.play())  # Deve imprimir "No media set"
    print(player.pause())  # Deve imprimir "No media set"
    print(player.stop())  # Deve imprimir "No media set"

elif test_case == "time_formatting":
    # Testa a formatação de tempo no método 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":
    # Testa se Song e Video implementam as interfaces corretamente
    song = Song("Test Song", "Test Artist", 180)
    video = Video("Test Video", "480p", 240)
    
    # Verifica a implementação da interface
    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)}")
    
    # Testa todos os métodos da 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":
    # Testa o comportamento polimórfico com uma lista de diferentes tipos de mídia
    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":
    # Testa casos extremos
    empty_song = Song("", "", 0)
    edge_video = Video("A" * 100, "", -10)  # Título muito longo, resolução vazia, duração negativa
    
    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":
    # Implementa um teste de estresse com muitos objetos de mídia
    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 com todas as músicas
    for i, song in enumerate(songs):
        player.set_media(song)
        if i % 10 == 0:  # Imprime apenas a cada 10 itens para evitar excesso de saída
            print(f"Playing song {i+1}: {player.play()}")
    
    # Teste com todos os vídeos
    for i, video in enumerate(videos):
        player.set_media(video)
        if i % 10 == 0:  # Imprime apenas a cada 10 itens para evitar excesso de saída
            print(f"Playing video {i+1}: {player.play()}")
    
    print("Stress test completed successfully!")
quiz iconTeste seus conhecimentos

Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.

Todas as lições de Object Oriented Programming