Implementación del patrón Chain of Responsibility en validaciones de pago

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

Resumen

¿Cómo aplicar el patrón Chain of Responsibility en un servicio de pagos?

El patrón Chain of Responsibility es una herramienta poderosa que permite crear cadenas de validaciones de manera flexible y estructurada. En esta guía, exploraremos cómo aplicar este patrón a un servicio de procesamiento de pagos, modificando su arquitectura para utilizar una cadena de responsabilidades que valide tanto el cliente como los datos de pago.

¿Cómo encapsular los datos necesarios para las validaciones?

Para comenzar, es necesario encapsular los datos del cliente y del pago en una clase que llamaremos Request. Utilizando la librería Pydantic con BaseModel, podemos crear esta estructura de datos fácilmente.

from pydantic import BaseModel
from payment_data import PaymentData
from customer import CustomerData

class Request(BaseModel):
    customerData: CustomerData
    paymentData: PaymentData

¿Cómo definir una clase abstracta para la cadena de validaciones?

El siguiente paso es definir una clase abstracta que dictará la forma que deberán tomar las validaciones dentro de la cadena. Utilizaremos el módulo ABC para crear una clase abstracta que denominaremos ChainHandler.

from abc import ABC, abstractmethod
from typing import Self
from dataclasses import dataclass

@dataclass
class ChainHandler(ABC):
    nextHandler: Self = None

    def setNext(self, handler: Self) -> Self:
        self.nextHandler = handler
        return handler

    @abstractmethod
    def handle(self, request: Request):
        pass

¿Cómo implementar la validación de clientes en la cadena?

Para integrar la validación de clientes, crearemos una clase CustomerHandler que extienda ChainHandler y utliza un validador previamente definido.

from validations import CustomerValidator

class CustomerHandler(ChainHandler):
    def handle(self, request: Request):
        validator = CustomerValidator()
        
        try:
            valid = validator.validate(request.customerData)
            if self.nextHandler:
                self.nextHandler.handle(request)
        except Exception as e:
            print("Error:", e)
            raise e

¿Cómo modificar el servicio para usar la cadena de validaciones?

Una vez que tengamos los Handlers, debemos modificar el servicio de pagos para utilizar la cadena de responsabilidades y no depender de válidus individuales.

from commons import Request

def some_service_method(customer_data, payment_data):
    try:
        request = Request(customerData=customer_data, paymentData=payment_data)
        validator.handle(request)
    except Exception as e:
        print("Error en las validaciones")
        raise e

¿Cómo ajustar el builder para construir la cadena de validaciones?

Para integrar el nuevo sistema de validaciones dentro del builder, añadiremos un método que configure la cadena de validación. Nos aseguramos de que el builder retorne el tipo correcto y se encadene correctamente.

from customer_handler import CustomerHandler
from typing import Optional

class PaymentServiceBuilder:
    validator: Optional[ChainHandler] = None

    def setChainOfValidations(self) -> Self:
        customer_handler = CustomerHandler()
        self.validator = customer_handler
        return self

    def build(self):
        # Lógica adicional de construcción
        ...

¿Cómo integrar la configuración de la cadena en la aplicación principal?

En el método principal donde se configura el servicio de pagos, eliminamos los antiguos métodos de configuración de validadores y añadimos el nuevo método para definir la cadena de validaciones.

builder = PaymentServiceBuilder()
builder.setChainOfValidations()

Este enfoque no solo mejora la organización del código, sino que también facilita la extensión de comportamiento al permitir añadir fácilmente más validadores a la cadena. Animamos a los estudiantes a seguir explorando y expandiendo esta implementación según sus necesidades específicas. ¡Continúen aprendiendo!