Patrones de Diseño y Principios SOLID

1

Patrones de Diseño y Principios SOLID en Python para Procesadores de Pago

Principios SOLID

2

Principio de Responsabilidad Única en Desarrollo de Software

3

Procesador de Pagos con Principios SOLID y Stripe

4

Aplicación del Principio de Responsabilidad Única en Procesador de Pagos

5

Principio Abierto-Cerrado en Desarrollo de Software

6

Implementación del Principio Abierto-Cerrado en Procesadores de Pago y Notificadores

7

Principio de Sustitución de Liskov en Desarrollo de Software

8

Aplicación del Principio de Sustitución de Liskov en Python

9

Principio de Segregación de Interfaces en Software

10

Implementación del Principio de Segregación de Interfaces en Procesadores de Pago

11

Principio de Inversión de Dependencias en Software

12

Aplicación del Principio de Inversión de Dependencias en Python

Reestructuración del proyecto

13

Reestructuración de Código con Módulos en Python y Principios SOLID

Patrones de Diseño

14

Introducción a los Patrones de Diseño de Software

15

Patrón Strategy en Diseño de Software con Python

16

Implementación del Strategy Pattern en Procesador de Pagos en Python

17

Patrón Factory Pattern en Python: Creación de Objetos Dinámicos

18

Patrón Factory en Procesadores de Pago en Python

19

Patrón Decorador: Añadir Responsabilidades Dinámicas a Objetos

20

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

21

Patrón de Diseño Builder: Construcción de Objetos Complejos

22

Builder Pattern para Servicio de Pagos en Python

23

Patrón Observer: Gestión de Eventos y Notificaciones Automáticas

24

Patrón Observer en Sistemas de Pago: Implementación y Notificaciones

25

Patrón Chain of Responsibility en Validación de Pagos

26

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

27

Principios SOLID y Patrones de Diseño en Procesadores de Pago

No tienes acceso a esta clase

¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera

Builder Pattern para Servicio de Pagos en Python

22/27
Recursos

¿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!

Aportes 6

Preguntas 2

Ordenar por:

¿Quieres ver más aportes, preguntas y respuestas de la comunidad?

Hola, quiero compartir **el resultado de mis reto**, me costó muchísimo :,v creación de **NotifierFactory**: ```python class NotifierFactory(): """_summary_ Args: NotifierProtocol: Interfaz que define el comportamiento de las notificaciones. customer_data (CustomerData): Información del cliente. ContactInfo: Trae la información de contacto del cliente. returns: Una instancia de la clase que se va a usar para notificar al cliente. """ @staticmethod def create_notifier(customer_data:CustomerData) -> NotifierProtocol: match customer_data.contact_info: case ContactInfo.email: return EmailNotifier() case ContactInfo.phone: return SMSNotifier(gateway="Tigo_Une: 1234567890") case _: raise ValueError("no se soporta este tipo de notificación") ```Instanciación de NotifierFactory en **Builder.py**: ```js def set_notifier(self, customer_data: CustomerData) -> Self: """_summary_ Args: customer_data: info de cliente Do: En Factory, se creó la lógica para elegir el notificador. Returns: Una clase (sms, email) """ self.notifier = NotifierFactory.create_notifier(customer_data) return self ``` *<u>MÉTODOS DE PAYMENT\_PROCESSOR</u>* Creación de **RefundPaymentFactory**: ```python @dataclass class RefundPaymentFactory(): payment_data: PaymentData @staticmethod def refundPayment(self, transaction_id:str): match self.payment_data.type: case PaymentType.ONLINE: match self.payment_data.currency: case "USD": return StripePaymentProcessor.refund_payment(transaction_id) case _: #cualquier otra cosa return LocalPaymentProcessor.refund_payment(transaction_id) ... case PaymentType.OFFLINE: return OfflinePaymentProcessor() #no hace falta instanciar el método. case _: # cualquier otra cosa raise ValueError("no se soporta este tipo de reembolsos") ```Instanciación de RefundPaymentFactory en **Builder.py:** ```js def set_refund_processor(self, transaction_id) -> Self: self.refund_processor = RefundPaymentFactory.refundPayment(transaction_id) return self ```Creación de **RecurringPaymentFactory:** ```python class RecurringPaymentFactory(): @staticmethod def setup_recurring(customer_data: CustomerData,payment_data:PaymentData): match payment_data.type: case PaymentType.ONLINE: match payment_data.currency: case "USD": return StripePaymentProcessor.setup_recurring_payment(customer_data, payment_data) case _: #cualquier otra cosa return LocalPaymentProcessor.setup_recurring_payment(customer_data, payment_data) ... case PaymentType.OFFLINE: return OfflinePaymentProcessor() #no hace falta instanciar el método. case _: # cualquier otra cosa raise ValueError("no se soporta este tipo de pagos recurrentes") ```Instanciación de RecurringPaymentFactory en **builder.py** ```python def set_recurring_payment(self, customer_data: CustomerData, payment_data: PaymentData) -> Self: self.recurring_processor = RecurringPaymentFactory.setup_recurring(customer_data=customer_data, payment_data=payment_data) return self ```Déjenme saber si me equivoqué jeheh @camilocsoto
Este no lo conocía y me va a servir mucho para mi bot de trading que estoy refactorizando. Muy bueno este curso, justo lo que necesitaba
Notifier Factory ```python from src.payment_service.commons import CustomerData from src.payment_service.notifiers import SMSNotifier, EmailNotifier, NotifierProtocol class NotifierFactory: @staticmethod def create_notifier(customer_data: CustomerData) -> NotifierProtocol: if customer_data.contact_info.phone: return SMSNotifier("YourSMSService") if customer_data.contact_info.email: return EmailNotifier() else: raise ValueError("No valid contact info provided") ```
¿Sería útil en este caso implementar una clase `Director` que se encargue de crear diferentes servicios ya predefinidos?
El \*\*Patrón Builder\*\* puede ser una excelente solución para construir servicios de pago que admiten configuraciones flexibles, como métodos de pago, monedas soportadas, opciones de validación antifraude y notificaciones. Veamos cómo implementar este patrón para un sistema de pagos. \--- \### Paso 1: Definir la Clase del Producto El producto será un objeto `ServicioPago` que representa el servicio configurado. ```python class ServicioPago: def \_\_init\_\_(self): self.metodo\_pago = None self.moneda = None self.validacion\_antifraude = False self.notificacion = False def \_\_str\_\_(self): return (f"Servicio de Pago configurado:\n" f" - Método de pago: {self.metodo\_pago}\n" f" - Moneda: {self.moneda}\n" f" - Validación antifraude: {self.validacion\_antifraude}\n" f" - Notificación: {self.notificacion}") ``` \--- \### Paso 2: Crear el Builder El `BuilderServicioPago` permitirá configurar las diferentes opciones del servicio paso a paso. ```python class BuilderServicioPago: def \_\_init\_\_(self): self.servicio = ServicioPago() def set\_metodo\_pago(self, metodo): self.servicio.metodo\_pago = metodo return self def set\_moneda(self, moneda): self.servicio.moneda = moneda return self def habilitar\_validacion\_antifraude(self): self.servicio.validacion\_antifraude = True return self def habilitar\_notificacion(self): self.servicio.notificacion = True return self def build(self): return self.servicio ``` \--- \### Paso 3: Usar el Builder para Crear un Servicio de Pago Ahora podemos crear servicios de pago con diferentes configuraciones de manera sencilla. ```python \# Crear el builder builder = BuilderServicioPago() \# Construir un servicio de pago básico servicio\_basico = ( builder .set\_metodo\_pago("Tarjeta de Crédito") .set\_moneda("USD") .build() ) print(servicio\_basico) \# Construir un servicio de pago avanzado servicio\_avanzado = ( builder .set\_metodo\_pago("PayPal") .set\_moneda("EUR") .habilitar\_validacion\_antifraude() .habilitar\_notificacion() .build() ) print("\n" + str(servicio\_avanzado)) ``` \*\*Salida esperada:\*\* ``` Servicio de Pago configurado: \- Método de pago: Tarjeta de Crédito \- Moneda: USD \- Validación antifraude: False \- Notificación: False Servicio de Pago configurado: \- Método de pago: PayPal \- Moneda: EUR \- Validación antifraude: True \- Notificación: True ``` \--- \### Paso 4: Añadir un Director para Configuraciones Comunes Un \*\*Director\*\* puede estandarizar la creación de configuraciones predefinidas. ```python class DirectorServicioPago: def \_\_init\_\_(self, builder): self.builder = builder def construir\_servicio\_basico(self): return ( self.builder .set\_metodo\_pago("Tarjeta de Débito") .set\_moneda("USD") .build() ) def construir\_servicio\_premium(self): return ( self.builder .set\_metodo\_pago("Stripe") .set\_moneda("GBP") .habilitar\_validacion\_antifraude() .habilitar\_notificacion() .build() ) ``` \#### Usar el Director ```python \# Crear el builder y el director builder = BuilderServicioPago() director = DirectorServicioPago(builder) \# Construir un servicio básico servicio\_basico\_director = director.construir\_servicio\_basico() print(servicio\_basico\_director) \# Construir un servicio premium servicio\_premium\_director = director.construir\_servicio\_premium() print("\n" + str(servicio\_premium\_director)) ``` \*\*Salida esperada:\*\* ``` Servicio de Pago configurado: \- Método de pago: Tarjeta de Débito \- Moneda: USD \- Validación antifraude: False \- Notificación: False Servicio de Pago configurado: \- Método de pago: Stripe \- Moneda: GBP \- Validación antifraude: True \- Notificación: True ``` \--- \### Ventajas del Patrón Builder en Servicios de Pago 1\. \*\*Flexibilidad\*\*: Puedes crear configuraciones específicas para cada cliente o integración. 2\. \*\*Modularidad\*\*: Los métodos encadenados permiten extender las opciones del servicio sin afectar las existentes. 3\. \*\*Cumplimiento de SOLID\*\*: \- \*\*Responsabilidad Única\*\*: Cada clase tiene una responsabilidad clara. \- \*\*Abierto/Cerrado\*\*: Puedes añadir nuevas opciones de configuración sin modificar el código existente. \--- \### Posibles Extensiones 1\. \*\*Validación personalizada\*\*: Agrega un método para verificar que todas las configuraciones requeridas estén completas antes de construir el objeto. 2\. \*\*Decoradores adicionales\*\*: Usa el Patrón Decorator para añadir características dinámicamente, como reportes de actividad o auditorías. 3\. \*\*Fluidez API\*\*: Implementa un sistema de configuración a partir de archivos JSON o YAML para integraciones externas. \--- \### Conclusión El \*\*Patrón Builder\*\* ofrece una manera estructurada y flexible de construir servicios de pago configurables. Es especialmente útil en aplicaciones empresariales donde las opciones de configuración varían según las necesidades del cliente o el sistema. Al combinarlo con otros patrones como Decorator o Factory, puedes lograr soluciones robustas y altamente reutilizables.
Para implementar un validador de parámetros como un decorador en lugar de incluirlo en el método `build`, puedes definir un decorador que valide cada parámetro antes de invocar el método de construcción. Aquí te dejo un ejemplo simplificado: ```python def validate_params(func): def wrapper(self): required_params = [self.payment_processor, self.notifier, self.customer_validator, self.payment_validator, self.logger] missing = [param for param in required_params if param is None] if missing: raise ValueError(f"Missing dependencies: {', '.join(missing)}") return func(self) return wrapper class PaymentServiceBuilder: ... @validate_params def build(self): return PaymentService( payment_processor=self.payment_processor, notifier=self.notifier, customer_validator=self.customer_validator, payment_validator=self.payment_validator, logger=self.logger ) ``` Este enfoque separa la lógica de validación de la construcción del servicio, facilitando su mantenimiento y reutilización.