Builder Pattern para Servicio de Pagos en Python

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

Resumen

¿Cómo implementar el patrón de diseño Builder para servicios de pagos?

El patrón Builder es una potente herramienta de diseño que facilita la construcción de objetos complejos al dividir el proceso en pequeños pasos más manejables y comprensibles. En esta clase, aplicaremos este patrón para estructurar un servicio de pagos, demostrando cómo crear un builder para el servicio de pagos y cómo implementar métodos set para definir sus propiedades. A medida que avanzamos, analizaremos su interacción con otros patrones como Factory, resaltando su flexibilidad y eficacia para lidiar con procesos avanzados de inicialización.

¿Cómo crear una clase Builder en Python?

Para comenzar con el patrón Builder en Python, la creacion de una nueva clase será nuestro primer paso. Esta clase se encargará de la provisión de todos los atributos requeridos para el servicio a implementar.

class PaymentServiceBuilder:
    def __init__(self):
        self.logger = None
        self.paymentValidator = None
        self.customerValidator = None
        self.paymentProcessor = None
        self.notifier = None

Es fundamental hacer que todos los atributos sean opcionales con un valor por defecto de None. Esto nos permitirá instanciarlos gradualmente con los métodos set.

¿Cómo crear métodos Set para cada atributo?

La creación de métodos set para inicializar cada atributo es un paso crucial. Estos métodos establecerán los componentes esenciales de manera secuencial:

  1. SetLogger: Inicializa un logger sin dependencias adicionales.

    def setLogger(self):
        self.logger = TransactionLogger()
        return self
    
  2. SetPaymentValidator y SetCustomerValidator: Ambos establecen validadores básicos.

    def setPaymentValidator(self):
        self.paymentValidator = PaymentDataValidator()
        return self
    
    def setCustomerValidator(self):
        self.customerValidator = CustomerValidator()
        return self
    
  3. Set del PaymentProcessor: Incorpora una fábrica para selección dinámica del procesador.

    def setPaymentProcessor(self, paymentData):
        self.paymentProcessor = PaymentProcessorFactory.createPaymentProcessor(paymentData)
        return self
    

¿Cómo manejar casos con más lógica?

Para atributos que dependen de otras condiciones, como notifier, utilizamos estructuras de control como if:

def setNotifier(self, customerData):
    if customerData.contactInfo.email:
        self.notifier = EmailNotifier()
    elif customerData.contactInfo.phoneNumber:
        self.notifier = SMSNotifier("MyCustomGateway")
    else:
        raise ValueError("No se puede seleccionar clase de notificación")
    return self

¿Cómo finalizamos el Builder?

Finalmente, creamos un método build para validar y construir el servicio completo. Validamos que los atributos no sean None antes de instanciar la clase PaymentService definitiva:

def build(self):
    required_fields = [self.paymentProcessor, self.notifier, 
                       self.customerValidator, self.paymentValidator, self.logger]
    if not all(required_fields):
        missing = [name for name, value in zip(["paymentProcessor", "notifier", 
                                                "customerValidator", "paymentValidator", "logger"], required_fields) if value is None]
        raise ValueError(f"Dependencias faltantes: {', '.join(missing)}")
    
    return PaymentService(self.paymentProcessor, self.paymentValidator, 
                          self.customerValidator, self.notifier, self.logger)

Implementación del Builder

Finalmente, veamos cómo el código se ejecuta en un flujo práctico de inicialización del servicio de pagos:

builder = PaymentServiceBuilder()
service = (builder.setLogger()
                  .setPaymentValidator()
                  .setCustomerValidator()
                  .setPaymentProcessor(paymentData)
                  .setNotifier(customerData)
                  .build())

Próximos pasos y desafíos

A medida que os volváis más cómodos con el patrón Builder, podéis intentar implementar funciones adicionales, como decoradores que modifiquen el comportamiento de vuestra clase principal. ¡Atrévete a experimentar y mejora contínuamente la modularidad y eficiencia de tu código!