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 seguir. 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 con solo 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 los 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 polimórficamente:
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:
def render_shape(drawable: Drawable):
return drawable.draw()
circle = Circle(3)
print(render_shape(circle))Salida:
Drawing a circlePunto clave: Las interfaces definen lo que las clases deben hacer, no cómo lo hacen. Usa clases base abstractas con solo métodos abstractos para crear contratos claros que las clases que los implementan 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.
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 completo de reproductor multimedia con una herencia adecuada y la implementación de interfaces.
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 predeterminado 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":
# Prueba el reproductor de medios sin medios establecidos
player = MediaPlayer()
print(player.play()) # Should print "No media set"
print(player.pause()) # Should print "No media set"
print(player.stop()) # Should print "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":
# Prueba que Song y Video implementen correctamente las interfaces
song = Song("Test Song", "Test Artist", 180)
video = Video("Test Video", "480p", 240)
# Comprobar la implementación de la interfaz
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":
# 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 cada décimo para evitar demasiada salida
print(f"Playing song {i+1}: {player.play()}")
# Prueba con todos los videos
for i, video in enumerate(videos):
player.set_media(video)
if i % 10 == 0: # Imprime solo cada décimo 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
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 bancariasPractica por tu cuenta: Compilador de Python online