Ernesto Vivas
de verdad que esto no se entiende absolutamente nada.
Adrian Yesid Restrepo
Completamente!
Mariany Mendoza
yo todo el curso
Aplicar el patrón decorador en Python con protocolos tipados aporta flexibilidad y control. Aquí verás cómo envolver un PaymentService con lógica de logging sin tocar el comportamiento original, usando typing.Protocol, composición con wrap y una implementación limpia con @dataclass.
¿Cómo aplicar el patrón decorador al PaymentService paso a paso?
Para extender un servicio de pagos sin modificarlo, se define una interfaz con Protocol, se implementa la clase principal conforme a esa interfaz y luego se crea un decorador que delega en el servicio real, añadiendo logging.
# service_protocol.py from typing import Protocol class PaymentServiceProtocol(Protocol): def process_transaction(self, customer_data, payment_data): ... def process_refund(self, transaction_id: str): ... def setup_recurring(self, customer_data, payment_data): ...
¿Qué protocolos y clases intervienen en el diseño?
Se definen dos abstracciones clave. Primero, el protocolo del servicio que actúa como contrato: especifica firmas, no comportamiento. Segundo, el protocolo del decorador que replica esas firmas y agrega el atributo wrap tipado como PaymentServiceProtocol para delegar la ejecución. Esto asegura sustitución transparente y facilita pruebas.
# decorator_protocol.py from typing import Protocol from service_protocol import PaymentServiceProtocol class PaymentServiceDecoratorProtocol(Protocol): wrap: PaymentServiceProtocol def process_transaction(self, customer_data, payment_data): ... def process_refund(self, transaction_id: str): ... def setup_recurring(self, customer_data, payment_data): ...
¿Cómo mejora el logging sin alterar la lógica original?
El decorador PaymentServiceLogging implementa el protocolo del decorador, usa @dataclass para su construcción y añade prints de inicio y fin alrededor de cada método. La clave es llamar a self.wrap con los mismos parámetros y retornar la respuesta original, manteniendo el comportamiento intacto.
# logging_service.py from dataclasses import dataclass from decorator_protocol import PaymentServiceDecoratorProtocol from service_protocol import PaymentServiceProtocol @dataclass class PaymentServiceLogging(PaymentServiceDecoratorProtocol): wrap: PaymentServiceProtocol def process_transaction(self, customer_data, payment_data): print("start process transaction") response = self.wrap.process_transaction(customer_data, payment_data) print("finish process transaction") return response def process_refund(self, transaction_id: str): print(f"start process refund: transaction_id={transaction_id}") response = self.wrap.process_refund(transaction_id) print("finish process refund") return response def setup_recurring(self, customer_data, payment_data): print("start setup recurring") response = self.wrap.setup_recurring(customer_data, payment_data) print("finish setup recurring") return response
¿Cómo se instancia el decorador en main?
# main.py (fragmento) from payment_service import PaymentService # implementación existente from logging_service import PaymentServiceLogging service = PaymentService(...) service = PaymentServiceLogging(wrap=service) service.process_transaction(customer_data, payment_data) service.process_refund("1234") # si el servicio real no implementa refund, fallará.
En la demostración, el intento de refund sirve para ilustrar el flujo decorado, aunque el servicio real no tenga ese procesador. La idea central se mantiene: extender funcionalidad (logging y depuración) sin modificar el código del servicio.
¿Qué habilidades y keywords refuerzas con este patrón?
¿De qué otras formas aplicarías el patrón decorador en tu PaymentService o en otras capas? Comparte tus ideas y casos en los comentarios.
Ernesto Vivas
de verdad que esto no se entiende absolutamente nada.
Adrian Yesid Restrepo
Completamente!
Mariany Mendoza
yo todo el curso
Abraham Nassiff
Cual es la ventaja de hacerlo así y no de la manera nativa de la que están definidos los decoradores en Python? Siento que la legilidad de esos está mejor y mas pythonic, pero de pronto estos ofrecen alguna ventaja sobre esos
Sasha Canal
Company_adminUn @decorador de python afecta solo la Función o el método de una clase
El Class Decorador, afecta toda la clase!
Ahora con un @decorador también podrías afectar todos los métodos de una clase.
Pero siento que es más declartivo hacer la creación de la instacia del Decorador que envuelve a la Instancia de la clase que queremos modificar...
Más fácil de leer, más claro...
Recuerda que el mejor código no es el más sofisticado sino el que sea más fácil de entender!
Rodrigo Martinez
La ventaja de usar el patrón decorador de esta forma (basado en clases e interfaces/protocolos) es que puedes agregar lógica compleja a los objetos existentes sin modificar su estructura, permitiendo mayor flexibilidad y mantenibilidad.
Ray Trápala
entonces, este patrón es un decorador más "sofisticado/robusto" podría decirse?
Eduardo José Álvarez
Team Platzino necesariamente, el patrón lo que permite es extender la funcionalidad base sin hacer modificaciones al código, no es más sofisticado en verdad.
De hecho lo que se ve en la clase se puede crear con decoradores de Python sin problema
Sasha Canal
Company_adminUn @decorador de python afecta solo la Función o el método de una clase
El Class Decorador, afecta toda la clase!
Ahora con un @decorador también podrías afectar todos los métodos de una clase.
Pero siento que es más declartivo hacer la creación de la instacia del Decorador que envuelve a la Instancia de la clase que queremos modificar...
Más fácil de leer, más claro...
Recuerda que el mejor código no es el más sofisticado sino el que sea más fácil de entender!
Mario Alexander Vargas Celis
Implementemos el **Patrón Decorator** para un sistema de pagos. Supongamos que tienes un servicio base que procesa pagos, y quieres añadir funcionalidades como registro de logs, validación antifraude y notificaciones sin modificar la clase original. ### Paso 1: Clase Base para el Servicio de Pagos Define una interfaz común para el servicio de pagos que será implementada por la clase base y los decoradores.
class PaymentService(ABC): @abstractmethod def process\_payment(self, amount: float, currency: str): pass``` Implementa la funcionalidad básica en una clase concreta. ```pythonclass BasicPaymentService(PaymentService): def process\_payment(self, amount: float, currency: str): print(f"Procesando pago de {amount} {currency}.")``` \### Paso 2: Crear Decoradores para Extender Funcionalidades Los decoradores implementan la misma interfaz y envuelven la instancia del servicio de pagos. \#### Decorador de Registro de Logs ```pythonclass LoggingDecorator(PaymentService): def \_\_init\_\_(self, payment\_service: PaymentService): self.payment\_service = payment\_service def process\_payment(self, amount: float, currency: str): print(f"\[Logger]: Procesando un pago de {amount} {currency}.") self.payment\_service.process\_payment(amount, currency)``` \#### Decorador de Validación Antifraude ```pythonclass FraudCheckDecorator(PaymentService): def \_\_init\_\_(self, payment\_service: PaymentService): self.payment\_service = payment\_service def process\_payment(self, amount: float, currency: str): if self.\_is\_fraudulent(amount): print("\[FraudCheck]: Pago marcado como fraudulento. Transacción detenida.") else: print("\[FraudCheck]: Pago validado.") self.payment\_service.process\_payment(amount, currency) def \_is\_fraudulent(self, amount: float) -> bool: # Simulación de reglas antifraude: Ejemplo, montos mayores a 10,000 son sospechosos return amount > 10000``` \#### Decorador de Notificación ```pythonclass NotificationDecorator(PaymentService): def \_\_init\_\_(self, payment\_service: PaymentService): self.payment\_service = payment\_service def process\_payment(self, amount: float, currency: str): self.payment\_service.process\_payment(amount, currency) self.\_send\_notification(amount, currency) def \_send\_notification(self, amount: float, currency: str): print(f"\[Notification]: Enviando notificación por el pago de {amount} {currency}.")``` \### Paso 3: Componer el Servicio Decorado Crea un servicio de pagos básico y añade las funcionalidades deseadas mediante decoradores. ```python# Servicio de pagos básicobasic\_service = BasicPaymentService() \# Añadir funcionalidad de registro de logslogged\_service = LoggingDecorator(basic\_service) \# Añadir validación antifraudefraud\_checked\_service = FraudCheckDecorator(logged\_service) \# Añadir notificaciónfull\_service = NotificationDecorator(fraud\_checked\_service) \# Usar el servicio decoradoprint("=== Pago 1 ===")full\_service.process\_payment(5000, "USD") print("\n=== Pago 2 ===")full\_service.process\_payment(15000, "USD")``` \### Resultado Esperado \*\*Pago 1 (válido):\*\*```\[Logger]: Procesando un pago de 5000 USD.\[FraudCheck]: Pago validado.Procesando pago de 5000 USD.\[Notification]: Enviando notificación por el pago de 5000 USD.``` \*\*Pago 2 (fraudulento):\*\*```\[Logger]: Procesando un pago de 15000 USD.\[FraudCheck]: Pago marcado como fraudulento. Transacción detenida.``` \### Ventajas del Patrón Decorator en este Contexto 1\. \*\*Modularidad\*\*: Cada funcionalidad (logs, antifraude, notificaciones) está separada en su propio decorador, lo que facilita su mantenimiento.2. \*\*Flexibilidad\*\*: Puedes añadir, quitar o combinar decoradores según sea necesario, sin modificar las clases originales.3. \*\*Cumple con SOLID\*\*: - \*\*Responsabilidad Única (SRP)\*\*: Cada decorador tiene una responsabilidad clara. - \*\*Abierto/Cerrado (OCP)\*\*: Puedes extender el comportamiento sin modificar las clases existentes. \### Extensión del Ejemplo \#### Añadir un Decorador para Conversión de Moneda Si necesitas convertir el monto a una moneda específica antes de procesarlo: ```pythonclass CurrencyConverterDecorator(PaymentService): def \_\_init\_\_(self, payment\_service: PaymentService, exchange\_rate: float): self.payment\_service = payment\_service self.exchange\_rate = exchange\_rate def process\_payment(self, amount: float, currency: str): converted\_amount = amount \* self.exchange\_rate print(f"\[CurrencyConverter]: Convertido {amount} {currency} a {converted\_amount} USD.") self.payment\_service.process\_payment(converted\_amount, "USD")``` Usa este decorador antes de los demás si necesitas convertir monedas. \### Conclusión El \*\*Patrón Decorator\*\* permite agregar funcionalidades como logs, validaciones y notificaciones al servicio de pagos de manera limpia y extensible. Es especialmente útil para aplicaciones empresariales donde los requisitos cambian con frecuencia y los servicios deben ser altamente configurables.