Aplicación del Patrón Decorador en Servicios de Pago

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

Resumen

¿Cómo aplicar el patrón decorador en un servicio de procesamiento de pagos?

Aplicar el patrón decorador puede hacer maravillas en nuestros proyectos de programación al permitir extender la funcionalidad de nuestras clases sin modificar su estructura original. En este contexto, expandimos un servicio de procesamiento de pagos para incluir funcionalidades adicionales como el registro detallado de las transacciones. A continuación, exploraremos paso a paso cómo implementar este patrón en un servicio de pagos.

¿Qué es un protocolo en Python?

Los protocolos en Python sirven como contratos que definen un conjunto esperado de métodos y atributos sin proporcionar su implementación. Utilizan la clase Protocol del módulo typing para crear una abstracción o interfaz que otras clases deben seguir.

from typing import Protocol

class PaymentServiceProtocol(Protocol):
    def process_transaction(self, customer_data, payment_data) -> None:
        ...

    def process_refund(self, transaction_id: str) -> None:
        ...

    def setup_recurrence(self) -> None:
        ...

Aquí hemos definido un protocolo para el servicio de pagos. Esta especificación asegurará que cualquier implementación concreta de un servicio de pagos (o decorador) siga los métodos definidos.

¿Cómo se implementa el protocolo en la clase principal?

La clase principal del servicio, generalmente llamada PaymentService, debe implementar el protocolo definido para garantizar que cumple con la interfaz esperada.

from payment_service_protocol import PaymentServiceProtocol

class PaymentService(PaymentServiceProtocol):
    def process_transaction(self, customer_data, payment_data):
        # Lógica para procesar la transacción
        pass

    def process_refund(self, transaction_id: str):
        # Lógica para procesar el reembolso
        pass

    def setup_recurrence(self):
        # Lógica para establecer recurrencias
        pass

¿Cómo crear el protocolo para decoradores?

El siguiente paso es definir un protocolo específico para los decoradores. Este protocolo también hereda de Protocol, asegurando que cualquier decorador implementado instancie y utilice la interfaz completa del servicio.

from payment_service_protocol import PaymentServiceProtocol

class PaymentServiceDecoratorProtocol(PaymentServiceProtocol):
    wrapped: PaymentServiceProtocol

Este protocolo establece que cualquier decorador contendrá el servicio original envuelto en una nueva capa de lógica.

¿Cómo implementar un decorador en el servicio?

Ahora viene la parte divertida: realmente implementar la lógica del decorador. Aquí extendemos la funcionalidad agregando registros o logs para rastrear cuándo se inician y terminan las transacciones.

from decorator_protocol import PaymentServiceDecoratorProtocol
from dataclasses import dataclass

@dataclass
class PaymentServiceLogin(PaymentServiceDecoratorProtocol):
    wrapped: PaymentServiceProtocol

    def process_transaction(self, customer_data, payment_data):
        print("Start process transaction")
        response = self.wrapped.process_transaction(customer_data, payment_data)
        print("Finish process transaction")
        return response

    def process_refund(self, transaction_id: str):
        print("Start process refund", transaction_id)
        response = self.wrapped.process_refund(transaction_id)
        print("Finish process refund", transaction_id)
        return response
    
    def setup_recurrence(self):
        print("Start setup recurrence")
        response = self.wrapped.setup_recurrence()
        print("Finish setup recurrence")
        return response

¿Cómo poner todo junto y probar?

Finalmente, en el archivo main, importamos e instanciamos nuestro decorador alrededor del servicio para probar su efectividad.

from login_service import PaymentServiceLogin
from services import SomePaymentServiceImplementation

# Instancia del servicio original
original_service = SomePaymentServiceImplementation()

# Aplicación del decorador
decorated_service = PaymentServiceLogin(wrapped=original_service)

# Ejecutar transacciones con la lógica decorada
decorated_service.process_transaction('customer_data', 'payment_data')
decorated_service.process_refund('12345')

¡Listo! Ahora el servicio de pago no solo procesa las transacciones de manera normal, sino que también ofrece detalles adicionales sobre el flujo de cada operación. Invito a todos los apasionados por Python a experimentar con este patrón y estoy ansioso por ver cómo podrías aplicarlo en diferentes escenarios. Y tú, ¿cómo mejorarías este ejemplo utilizando decoradores de Python?