Principio de inversión de dependencias: servicio de pagos flexible

Clase 12 de 27Curso de Patrones de Diseño y SOLID en Python

Resumen

Aplicar el principio de inversión de dependencias en un servicio de pagos permite cambiar procesadores como Stripe u opciones offline sin tocar la lógica de alto nivel. Aquí verás cómo componer el Payment Service con protocolos, eliminar default factories e introducir inyección de dependencias para manejar cobros, recurrencias y reembolsos de forma limpia.

¿Qué asegura el principio de inversión de dependencias en el servicio de pagos?

El servicio (clase de alto nivel) no depende de detalles de clases de bajo nivel, sino de interfaces; en Python, de protocolos. Los detalles viven en los procesadores de pago que hablan con distintas pasarelas. Si se cambia el protocolo, se ajustan las implementaciones, pero el servicio se mantiene estable.

  • Clase de alto nivel: Payment Service. Depende de protocolos, no de implementaciones concretas.
  • Clases de bajo nivel: procesadores de pago, notificador, validadores, logger.
  • Protocolos en Python: definen el contrato. La implementación puede variar sin afectar al servicio.
  • Recurrencia y reembolsos: pueden usar la misma instancia del procesador (por ejemplo, Stripe) si cumple los protocolos.
  • Beneficio clave: menor acoplamiento y mayor facilidad de reemplazo.

¿Cómo instanciar dependencias sin default factories?

Para cumplir a fondo el principio, se eliminan los default factories del servicio: la clase de alto nivel no debe saber cómo instanciar clases de bajo nivel. En su lugar, se reciben como dependencias ya construidas.

# Dependencias de bajo nivel
stripe_processor = StripePaymentProcessor()
email_notifier = EmailNotifier()
customer_validator = CustomerValidator()
payment_validator = PaymentValidator()
logger = TransactionLogger()

# Clase de alto nivel: composición explícita
payment_service = PaymentService(
    payment_processor=stripe_processor,
    notifier=email_notifier,
    customer_validator=customer_validator,
    payment_validator=payment_validator,
    logger=logger,
    recurrence_processor=stripe_processor,   # reutiliza Stripe para recurrencia
    refund_processor=stripe_processor        # reutiliza Stripe para reembolsos
)
  • default factories: se remueven del servicio para evitar acoplar construcción con lógica.
  • Instanciación limpia: cada dependencia se crea fuera y se inyecta.
  • Reutilización: Stripe cumple protocolos de recurrencia y reembolso, por eso se usa la misma instancia.

¿Cómo cambiar implementaciones con inyección de dependencias?

La inyección de dependencias permite intercambiar implementaciones sin modificar el servicio. Esto abre la puerta a usar un contenedor de inyección de dependencias en escenarios reales, aunque aquí se simula con composición manual.

# Nuevas implementaciones de bajo nivel
offline_processor = OfflineProcessor()
sms_notifier = SMSNotifier(gateway="custom_gateway")

# Segundo servicio con otras dependencias y sin recurrencia/reembolsos
second_service = PaymentService(
    payment_processor=offline_processor,
    notifier=sms_notifier,
    customer_validator=customer_validator,
    payment_validator=payment_validator,
    logger=logger
)
  • Intercambiabilidad: cambia Stripe por offline processor sin tocar la lógica de alto nivel.
  • Notificadores: alterna entre Email Notifier y SMS notifier según necesidad.
  • Configuración: elimina recurrencia y reembolsos cuando no aplican.

¿Qué habilidades y conceptos practicas?

  • Diferenciar clase de alto nivel vs clases de bajo nivel.
  • Programar contra interfaces/protocolos para reducir acoplamiento.
  • Eliminar default factories en el servicio para respetar el principio.
  • Aplicar inyección de dependencias para componer servicios flexibles.
  • Reutilizar instancias cuando cumplen múltiples protocolos: menos complejidad.
  • Preparar el terreno para un contenedor de inyección de dependencias más adelante.

¿De qué forma aplicarías el principio de inversión de dependencias en tu propio flujo de pagos o notificaciones? Comparte tu enfoque y, si te animas, integra los casos de cobro, recurrencia y reembolso practicando esta misma composición de dependencias.