Patrón Factory en Procesadores de Pago en Python

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

Resumen

¿Cómo implementar el patrón Factory para mejorar la creación de clases?

El patrón Factory es una técnica poderosa en programación que permite encapsular el comportamiento de creación de clases, favoreciendo la reutilización del código y facilitando la mantenibilidad de los programas. Al centralizar la lógica de creación, podemos tomar decisiones más informadas y reducir el acoplamiento en nuestros sistemas. Aquí exploramos cómo implementarlo en Python, usando el ejemplo de un sistema de procesadores de pago.

¿Cómo se estructura el sistema de procesadores de pago?

Para gestionar diferentes tipos de pagos, es crucial contar con una estructura que permita identificar qué procesador utilizar dependiendo de las características del pago, tales como su tipo y moneda. Esto se logra a través de clases y métodos bien definidos.

  • Tipos de pago: Se define un tipo de pago a través de un enum, pudiendo ser offline u online. Este tipo de datos es parte del conjunto PaymentData, crucial para identificar el método de procesamiento adecuado.
  • Procesadores disponibles: Existen tres tipos básicos de procesadores de pago:
    • Procesador local.
    • Procesador offline.
    • Procesador Stripe.
  • Lógica de selección: Dependiendo del tipo de pago y la moneda, se selecciona el procesador adecuado. Por ejemplo, un pago online en dólares selecciona el procesador Stripe, mientras que otras monedas optan por el local.

¿Cómo desarrollar la clase PaymentProcessorFactory?

La clase PaymentProcessorFactory destaca por su capacidad de abstractar la lógica de selección del procesador de pago mediante el uso del operador match de Python, uno de los recientes agregados para simplificar la toma de decisiones.

class PaymentProcessorFactory:

    @staticmethod
    def create_payment_processor(payment_data: PaymentData) -> PaymentProcessorProtocol:
        match payment_data.type:
            case PaymentType.OFFLINE:
                return OfflinePaymentProcessor()
            case PaymentType.ONLINE:
                match payment_data.currency:
                    case "USD":
                        return StripePaymentProcessor()
                    case _:
                        return LocalPaymentProcessor()
            case _:
                raise ValueError("Tipo de pago no soportado")

Características principales:

  • Método estático: Define un método estático CreatePaymentProcessor, que toma como único argumento a PaymentData.
  • Operador match: Facilita la selección del procesador de pago deseado según condiciones predefinidas.
  • Excepciones gestionadas: Cuando un tipo de pago no es reconocido, se lanza una excepción ValueError, asegurando que solo se gestionen tipos de pago válidos.

¿Cómo integrar la fábrica en el servicio de pagos?

El servicio de pagos se puede optimizar para utilizar la clase PaymentProcessorFactory, mejorando así la claridad y la capacidad de mantenimiento del código.

class PaymentService:

    @classmethod
    def create_with_payment_processor(cls, payment_data: PaymentData, **kwargs) -> 'PaymentService':
        try:
            processor = PaymentProcessorFactory.create_payment_processor(payment_data)
            return cls(payment_processor=processor, **kwargs)
        except ValueError as e:
            print("Error creando la clase")
            raise e

Detalles de interés:

  • Class Method: create_with_payment_processor es un método de clase que permite la creación del servicio incluyendo el procesador adecuado según PaymentData.
  • Diccionario de atributos: Utiliza **kwargs para aceptar un conjunto flexible de parámetros adicionales, dispersándolos en la clase instanciada.
  • Manejo de errores: A través del bloque try-except, se captura cualquier ValueError que pueda surgir durante la creación del procesador, brindando feedback inmediato.

Pruebas y depuración del proceso

Para verificar el funcionamiento correcto del sistema, y del patrón Factory en particular, se deben seguir pasos clave como:

  • Datos de pago de ejemplo: Crear una instancia de PaymentData con valores como cantidad, moneda y tipo, permitiendo explorar los diferentes flujos de lógica.
  • Instanciación del servicio: Usar PaymentService.create_with_payment_processor para generar un servicio de pago y analizar el comportamiento esperado.
  • Debugging: Utilizar breakpoints para inspeccionar paso a paso cómo se asignan los procesadores según las características de pago.

Aplicando correctamente el patrón Factory en este escenario, se logra una estructura de código escalable y eficaz, alentando a los desarrolladores a continuar explorando y aplicando patrones de diseño en sus proyectos.