Diseño de interfaces
Parte de la sección Object Oriented Programming del Journey de Python de Coddy — lección 26 de 64.
Una interfaz define un contrato que las clases deben seguir. En Python, creamos interfaces utilizando clases base abstractas donde 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 en una interfaz deben ser abstractos: definen lo que las clases que las implementan deben hacer, 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 sugerencias de tipo (type hints):
def render_shape(drawable: Drawable):
return drawable.draw()
circle = Circle(3)
print(render_shape(circle))Resultado:
Drawing a circlePunto clave: Las interfaces definen qué deben hacer las clases, no cómo lo hacen. Utilice clases base abstractas con solo métodos abstractos para crear contratos claros que las clases implementadoras deban seguir. Esto garantiza un comportamiento consistente a través de diferentes implementaciones.
Desafío
IntermedioEn este desafío, implementarás un sistema de reproductor de medios utilizando interfaces.
Debes 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 TODO detallados para guiar tu implementación. Sigue estos comentarios para crear un sistema de reproductor de medios completo con la herencia y la implementación de interfaces adecuadas.
Hoja de referencia
Crea interfaces utilizando clases base abstractas con el módulo abc:
from abc import ABC, abstractmethodDefine una interfaz solo con métodos abstractos:
class Drawable(ABC):
@abstractmethod
def draw(self):
pass
@abstractmethod
def resize(self, width, height):
passImplementa la interfaz en clases 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}"Usa interfaces de forma polimórfica:
shapes = [Circle(5), Rectangle(3, 4)]
for shape in shapes:
print(shape.draw())
print(shape.resize(10, 8))Usa interfaces como pistas de tipo (type hints):
def render_shape(drawable: Drawable):
return drawable.draw()Las interfaces definen qué deben hacer las clases, no cómo lo hacen. Todos los métodos en una interfaz deben ser abstractos para crear contratos claros para las clases que los implementan.
Pruébalo tú mismo
from song import Song
from video import Video
from mediaplayer import MediaPlayer
from playable import Playable, MediaInfo
# Manejador integral de casos de prueba
test_case = input()
if test_case == "default_test":
# Caso de prueba por defecto del problema original
song = Song("Bohemian Rhapsody", "Queen", 355)
video = Video("Python Tutorial", "1080p", 1800)
player = MediaPlayer()
# Prueba con canción
player.set_media(song)
print(player.current_media.get_info())
print(player.play())
print(player.pause())
print(player.stop())
print() # Línea vacía para legibilidad
# Prueba con 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":
# Probar el reproductor de medios 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":
# Probar 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":
# Probar que Song y Video implementan correctamente las interfaces
song = Song("Test Song", "Test Artist", 180)
video = Video("Test Video", "480p", 240)
# Verificar implementación de 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)}")
# Probar todos los métodos de la interfaz
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":
# Probar el comportamiento polimórfico con una lista de diferentes 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":
# Probar 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":
# Implementar una prueba de esfuerzo con muchos objetos de medios
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()
# Probar con todas las canciones
for i, song in enumerate(songs):
player.set_media(song)
if i % 10 == 0: # Imprimir solo cada 10 para evitar demasiada salida
print(f"Playing song {i+1}: {player.play()}")
# Probar con todos los videos
for i, video in enumerate(videos):
player.set_media(video)
if i % 10 == 0: # Imprimir solo 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 Object Oriented Programming
1Fundamentos de la POO
Archivos externosIntroducción a la POOClases vs ObjetosEl parámetro selfMétodosAtributosMétodo constructor (__init__)Resumen - Calculadora simple4Herencia
Herencia básicaLa función super()Sobrescritura de métodosHerencia múltipleOrden de resolución de métodosResumen - 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 propertyDecorador de métodos estáticosDecorador de métodos de clase5Polimorfismo
Sobreescritura de métodos revisitadaDuck TypingClases y métodos abstractosDiseño de interfacesResumen - Calculadora de figuras8Conceptos avanzados de POO
Composición vs. 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 de claseDecoradores de propiedadesAtributos privadosResumen - Gestor de cuentas bancarias