Diseño de interfaces
Parte de la sección Programación orientada a objetos del Journey de Python de Coddy. Lección 26 de 64.
Una interfaz define un contrato que las clases deben cumplir. En Python, creamos interfaces mediante clases base abstractas en las que todos los métodos son abstractos.
Importa el módulo abc:
from abc import ABC, abstractmethodCrea una interfaz solo con métodos abstractos:
class Drawable(ABC):
@abstractmethod
def draw(self):
pass
@abstractmethod
def resize(self, width, height):
passTodos los métodos de una interfaz deben ser abstractos: definen lo que deben hacer las clases que la implementan, no cómo hacerlo.
Implementa la interfaz en una clase 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}"Crea otra clase que implemente la misma interfaz:
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}"Usa la interfaz de forma polimórfica:
shapes = [Circle(5), Rectangle(3, 4)]
for shape in shapes:
print(shape.draw())
print(shape.resize(10, 8))Salida:
Drawing a circle
Resized circle to radius 4.0
Drawing a rectangle
Resized rectangle to 10x8También puedes usar interfaces como anotaciones de tipo:
def render_shape(drawable: Drawable):
return drawable.draw()
circle = Circle(3)
print(render_shape(circle))Salida:
Drawing a circlePunto clave: Las interfaces definen qué deben hacer las clases, no cómo lo hacen. Usa clases base abstractas con solo métodos abstractos para crear contratos claros que las clases implementadoras deben seguir. Esto garantiza un comportamiento coherente entre distintas implementaciones.
Desafío
IntermedioEn este desafío, implementarás un sistema de reproductor multimedia con interfaces.
Tienes que editar los siguientes archivos:
playable.py- Implementa las clases base abstractas (interfaces)song.py- Implementa la clase Songvideo.py- Implementa la clase Videomediaplayer.py- Implementa la clase MediaPlayer
Cada archivo contiene comentarios detallados de TODO para guiar tu implementación. Sigue estos comentarios para crear un sistema de reproductor multimedia completo con la herencia y la implementación de interfaces adecuadas.
Pruébalo tú mismo
from song import Song
from video import Video
from mediaplayer import MediaPlayer
from playable import Playable, MediaInfo
# Gestor completo de casos de prueba
test_case = input()
if test_case == "default_test":
# Caso de prueba predeterminado del problema original
song = Song("Bohemian Rhapsody", "Queen", 355)
video = Video("Python Tutorial", "1080p", 1800)
player = MediaPlayer()
# Prueba con una canción
player.set_media(song)
print(player.current_media.get_info())
print(player.play())
print(player.pause())
print(player.stop())
print() # Línea en blanco para facilitar la lectura
# Prueba con un 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":
# Prueba el reproductor multimedia sin ningún medio configurado
player = MediaPlayer()
print(player.play()) # Debería imprimir "No media set"
print(player.pause()) # Debería imprimir "No media set"
print(player.stop()) # Debería imprimir "No media set"
elif test_case == "time_formatting":
# Prueba el formato de tiempo en el 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":
# Comprueba que Song y Video implementen correctamente las interfaces
song = Song("Test Song", "Test Artist", 180)
video = Video("Test Video", "480p", 240)
# Comprueba la implementación de las 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)}")
# Prueba todos los métodos de las interfaces
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":
# Prueba el comportamiento polimórfico con una lista de distintos tipos de medios
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":
# Prueba los casos límite
empty_song = Song("", "", 0)
edge_video = Video("A" * 100, "", -10) # Título muy largo, resolución vacía, duración 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 una prueba de estrés con muchos objetos multimedia
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()
# Prueba con todas las canciones
for i, song in enumerate(songs):
player.set_media(song)
if i % 10 == 0: # Imprime solo uno de cada 10 para evitar demasiada salida
print(f"Playing song {i+1}: {player.play()}")
# Prueba con todos los vídeos
for i, video in enumerate(videos):
player.set_media(video)
if i % 10 == 0: # Imprime solo uno de cada 10 para evitar demasiada salida
print(f"Playing video {i+1}: {player.play()}")
print("Stress test completed successfully!")Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Programación orientada a objetos
1Fundamentos de la POO
Introducción a la POOArchivos externosClases vs. objetosEl parámetro selfMétodosAtributosMétodo constructor (__init__)Repaso: calculadora sencilla4Herencia
Herencia básicaLa función super()Sobrescritura de métodosHerencia múltipleOrden de resolución de métodosRepaso: jerarquía de empleados7Métodos especiales
Introducción a los métodos mágicosSobrecarga de operadoresMétodos mágicos de contenedoresRepaso: lista personalizada10Patrones de diseño, parte 1
Introducción a los patrones de diseñoPatrón SingletonPatrón FactoryPatrón ObserverPatrón Strategy2Decoradores
Introducción a los decoradoresDecorador de propiedadDecorador de método estáticoDecorador de método de clase5Polimorfismo
Repaso de la sobrescritura de métodosTipado patoClases y métodos abstractosDiseño de interfacesRepaso: calculadora de formas8Conceptos avanzados de POO
Composición frente a herenciaMixinsMétodos estáticos y de claseDecoradores de claseGestores de contexto11Patrones de diseño, parte 2
Patrón CommandPatrón AdapterPatrón DecoratorPatrón Template MethodPatrón StatePatrón Composite3Propiedades de clase
Variables de instancia vs. variables de claseDecoradores de propiedadesAtributos privadosRepaso: gestor de cuentas bancariasPractica por tu cuenta: Compilador de Python online