Principio de sustitución de Liskov en Python
Clase 8 de 27 • Curso de Patrones de Diseño y SOLID en Python
Contenido del curso
Principios SOLID
- 2

Principio de responsabilidad única en SOLID
05:59 min - 3

Refactorizando código Python con principios SOLID
11:14 min - 4

Cómo aplicar SRP en un procesador de pagos con Stripe
25:19 min - 5

Open Closed Principle: extensión sin modificación
02:39 min - 6

Cómo usar clases abstractas en Python
14:46 min - 7

Principio de Liskov en S.O.L.I.D.
03:18 min - 8

Principio de sustitución de Liskov en Python
Viendo ahora - 9

Interface Segregation: cuándo separar contratos
02:33 min - 10

Segregación de interfaces en procesadores de pagos
09:05 min - 11

Principio de inversión de dependencias explicado
04:13 min - 12

Principio de inversión de dependencias: servicio de pagos flexible
05:56 min
Reestructuración del proyecto
Patrones de Diseño
- 14

Qué son los patrones de diseño: definición y categorías
03:54 min - 15

Strategy Pattern con Python y setprocessor
01:55 min - 16

Strategy Pattern para pagos en Python
10:58 min - 17

Factory Pattern: centralizar creación de objetos
03:05 min - 18

Patrón Factory para procesar pagos con match
11:06 min - 19

Patrón Decorator en 5 pasos para funcionalidad dinámica
03:06 min - 20

Patrón decorador en servicios de pagos
12:57 min - 21

Builder Pattern: construcción paso a paso
01:28 min - 22

Builder pattern para servicios de pagos
18:55 min - 23

Observer Pattern en sistemas de eventos
01:48 min - 24

Observer en sistemas de pagos con Python
11:11 min - 25

Chain of Responsibility para validar pagos
02:04 min - 26

Chain of Responsibility en servicios de pagos
16:27 min - 27

Arquitectura robusta para procesadores de pago
03:19 min
Aprende a aplicar el principio de sustitución de Liskov (LSP) en Python usando protocolos, detectando y corrigiendo un bug real en un notificador por SMS. Verás cómo usar docstrings en formato NumPy, aprovechar un default factory y garantizar que las clases sean intercambiables sin romper el servicio de pagos.
¿Qué problema rompe el principio de sustitución de Liskov?
En el proyecto se reemplazaron las clases abstractas por protocolos (interfaces en Python) para Notifier y Payment Processor. Se añadieron docstrings que documentan comportamiento, parámetros y tipos, usando el formato NumPy en send_confirmation y describiendo también process_transaction.
Se introdujo a propósito un bug para ilustrar LSP: EmailNotifier respeta la firma del método send_confirmation(customer_data), tal como exige el protocolo, pero SMSNotifier exige un parámetro adicional sms_gateway. Al inyectar SMSNotifier en el servicio de pagos —que por defecto notifica por email mediante un default factory—, el debugger mostró: validación de cliente y pago correctas, cargo en Stripe correcto, pero una excepción al ejecutar SMSNotifier por incompatibilidad de firma. Esto impide sustituir email por SMS sin cambios en el servicio, violando LSP.
- Protocolos: forma de declarar interfaces en Python con comportamiento similar a clases abstractas.
- Docstrings: documentación del comportamiento y tipos; se usó formato NumPy.
- Firma del método: el protocolo define
send_confirmation(customer_data). - Bug:
SMSNotifierpedíasms_gatewaycomo parámetro y rompía la sustitución.
¿Cómo se veía la firma incompatible?
from typing import Protocol
class Notifier(Protocol):
def send_confirmation(self, customer_data):
...
class EmailNotifier:
def send_confirmation(self, customer_data):
"""
Parameters
----------
customer_data : dict
Datos del cliente para confirmar la transacción.
"""
pass
class SMSNotifier:
# Incumple el protocolo: agrega un parámetro extra.
def send_confirmation(self, customer_data, sms_gateway): # rompe LSP
pass
¿Cómo se corrige con dataclass y atributos?
La solución fue convertir SMSNotifier en data class y mover sms_gateway a un atributo de instancia. Así, el método respeta la firma del protocolo y usa el atributo interno.
from dataclasses import dataclass
@dataclass
class SMSNotifier:
sms_gateway: str # Atributo en lugar de parámetro del método.
def send_confirmation(self, customer_data):
# Aquí se usaría self.sms_gateway para enviar el SMS.
pass
# Instanciación correcta del notificador SMS.
sms_notifier = SMSNotifier(sms_gateway="this is a SMS mock get")
Con este ajuste, EmailNotifier y SMSNotifier comparten la misma firma y son sustituibles.
¿Cómo se prueba la sustitución en el servicio de pagos?
En la implementación del servicio se creó un notificador por SMS y otro por email. El servicio de pagos, por defecto, usa el notificador de email gracias al default factory, pero se puede pasar el notificador SMS por parámetro. Con un breakpoint en la sección clave y usando el debugger se verificó: validación de cliente y pago correctas, cargo en Stripe, y tras la corrección, no se generó ninguna excepción. Luego se probó cambiando al notificador por email y todo funcionó de forma intercambiable, sin modificar el servicio.
¿Qué habilidades y conceptos se refuerzan?
- Principio de sustitución de Liskov: clases hijas o implementaciones deben ser intercambiables sin romper el cliente.
- Protocolos en Python: alternativa ligera a clases abstractas para definir interfaces.
- Docstrings en formato NumPy: claridad en comportamiento, parámetros y tipos.
- Firma consistente: evitar parámetros extra que rompen la interfaz.
- Data class: mover dependencias a atributos de instancia.
- Instanciación correcta: pasar el
sms_gatewayal crearSMSNotifier. - Pruebas con debugger y breakpoint: validar flujo y detectar excepciones.
- Configuración por defecto con default factory: email como estrategia de notificación base.
¿Qué errores detectarías y cómo mejorarlos?
- Verifica que todas las implementaciones de
Notifierrespeten la misma firma. - Asegura que los servicios acepten cualquier notificador sin cambios internos.
- Documenta con docstrings los tipos esperados y valores de retorno.
- Repite la prueba con ambos notificadores para confirmar la intercambiabilidad.
¿De qué otra forma aplicarías Liskov aquí? ¿Qué otros detalles mejorarías en el código? Comparte tu enfoque en los comentarios.