Builder pattern para servicios de pagos
Clase 22 de 27 • Curso de Patrones de Diseño y SOLID en Python
Contenido del curso
Principios SOLID
- 2

Principio de responsabilidad única en SOLID
05:59 min - 3

Refactorizando código Python con principios SOLID
11:14 min - 4

Cómo aplicar SRP en un procesador de pagos con Stripe
25:19 min - 5

Open Closed Principle: extensión sin modificación
02:39 min - 6

Cómo usar clases abstractas en Python
14:46 min - 7

Principio de Liskov en S.O.L.I.D.
03:18 min - 8

Principio de sustitución de Liskov en Python
06:38 min - 9

Interface Segregation: cuándo separar contratos
02:33 min - 10

Segregación de interfaces en procesadores de pagos
09:05 min - 11

Principio de inversión de dependencias explicado
04:13 min - 12

Principio de inversión de dependencias: servicio de pagos flexible
05:56 min
Reestructuración del proyecto
Patrones de Diseño
- 14

Qué son los patrones de diseño: definición y categorías
03:54 min - 15

Strategy Pattern con Python y setprocessor
01:55 min - 16

Strategy Pattern para pagos en Python
10:58 min - 17

Factory Pattern: centralizar creación de objetos
03:05 min - 18

Patrón Factory para procesar pagos con match
11:06 min - 19

Patrón Decorator en 5 pasos para funcionalidad dinámica
03:06 min - 20

Patrón decorador en servicios de pagos
12:57 min - 21

Builder Pattern: construcción paso a paso
01:28 min - 22

Builder pattern para servicios de pagos
Viendo ahora - 23

Observer Pattern en sistemas de eventos
01:48 min - 24

Observer en sistemas de pagos con Python
11:11 min - 25

Chain of Responsibility para validar pagos
02:04 min - 26

Chain of Responsibility en servicios de pagos
16:27 min - 27

Arquitectura robusta para procesadores de pago
03:19 min
Aplicar el patrón builder en Python permite construir un servicio de pagos flexible, seguro y fácil de mantener. Aquí verás cómo crear un PaymentServiceBuilder con atributos opcionales, métodos encadenables, validaciones robustas y la integración con un factory para seleccionar el procesador de pago correcto y un notificador por email o SMS.
¿Qué problema resuelve el builder pattern en un servicio de pagos?
El escenario requiere muchas configuraciones previas a la instanciación del servicio. El builder organiza estos pasos, hace explícitas las dependencias y evita errores de inicialización.
- Muchos pasos previos: procesador, notificador, validadores y logger.
- Atributos opcionales con valor None hasta completar la configuración.
- Encadenamiento de métodos que retornan self para una construcción fluida.
- Validación previa con un método build que asegura dependencias completas.
- Combinación de patrones: factory para seleccionar el payment processor y builder para orquestar la construcción.
¿Cómo implementar el builder del PaymentService paso a paso?
La clase del builder replica los atributos del servicio final, pero los define como opcionales con valor por defecto None. Se usa data class para simplificar.
¿Cómo se declaran atributos opcionales con data class?
from dataclasses import dataclass
from typing import Optional, Self
# Protocolos/clases referenciadas como tipos (ya importadas en tu proyecto)
# PaymentProcessorProtocol, TransactionLogger, PaymentDataValidator, CustomerDataValidator
@dataclass
class PaymentServiceBuilder:
logger: Optional[TransactionLogger] = None
payment_validator: Optional[PaymentDataValidator] = None
customer_validator: Optional[CustomerDataValidator] = None
payment_processor: Optional[PaymentProcessorProtocol] = None
notifier: Optional[object] = None # EmailNotifier o SMSNotifier
- Mismos atributos que el servicio final pero con Optional y None.
- Importaciones de protocolos y clases necesarias para tipado y funcionamiento.
¿Cómo encadenar métodos set y qué se instancia directo?
Logger y validadores no dependen de abstracciones externas, se instancian directo.
def setLogger(self) -> Self:
self.logger = TransactionLogger()
return self
def setPaymentValidator(self) -> Self:
self.payment_validator = PaymentDataValidator()
return self
def setCustomerValidator(self) -> Self:
self.customer_validator = CustomerDataValidator()
return self
- Cada set retorna self: permite encadenar llamadas.
- Instanciación directa: sin lógica adicional.
¿Cómo integrar factory y notificador según datos?
Para el procesador se usa un factory; para el notificador, reglas con if.
# from commons import PaymentData
# from factory import PaymentProcessorFactory
def setPaymentProcessor(self, payment_data: PaymentData) -> Self:
self.payment_processor = PaymentProcessorFactory.create_payment_processor(payment_data)
return self
# from commons import CustomerData
# importa EmailNotifier y SMSNotifier según tu estructura
def setNotifier(self, customer_data: CustomerData) -> Self:
contact = customer_data.contact_info
if contact.email:
self.notifier = EmailNotifier()
return self
if contact.phone_number:
self.notifier = SMSNotifier(gateway="my custom gateway")
return self
raise ValueError("No se puede seleccionar clase de notificación.")
- Selección por factory: decide el mejor procesador según el tipo de pago.
- Reglas de notificación: email o SMS según contact info.
- Excepción temprana: si no hay email ni teléfono.
¿Qué valida build y cómo maneja errores?
El método build verifica que las dependencias críticas estén presentes antes de instanciar el servicio. Si falta algo, informa exactamente qué falta.
# from service import PaymentService
def build(self) -> PaymentService:
required = [
self.payment_processor,
self.notifier,
self.customer_validator,
self.payment_validator,
self.logger,
]
if not all(required):
# pares (nombre, valor) para identificar faltantes
pairs = [
("payment_processor", self.payment_processor),
("notifier", self.notifier),
("customer_validator", self.customer_validator),
("payment_validator", self.payment_validator),
("logger", self.logger),
]
missing = [name for name, value in pairs if value is None]
raise ValueError(f"missing dependencies: {missing}")
# El editor puede advertir por tipos Optional; ya fueron validados arriba.
return PaymentService(
payment_processor=self.payment_processor,
payment_validator=self.payment_validator,
customer_validator=self.customer_validator,
notifier=self.notifier,
logger=self.logger,
)
- Chequeo con all: si hay None en requeridos, falla la validación.
- Lista missing: indica en claro qué dependencia falta.
- Instanciación final segura: solo ocurre con dependencias completas.
¿Cómo usar el builder y qué ocurre si falta un set?
Encadena sets y finaliza con build. Si omites un set requerido, build lanza ValueError con la dependencia faltante.
# from builder import PaymentServiceBuilder
# payment_data y customer_data ya preparados
builder = PaymentServiceBuilder()
service = (
builder
.setLogger()
.setPaymentValidator()
.setCustomerValidator()
.setPaymentProcessor(payment_data)
.setNotifier(customer_data)
.build()
)
# Si omites, por ejemplo, .setPaymentValidator():
# ValueError: missing dependencies: ['payment_validator']
- Encadenamiento fluido: claridad y orden en la configuración.
- Errores claros: facilitan depurar con breakpoints y corregir rápido.
- Extensión sugerida: añade sets para procesadores de refund y recurring.
¿Te animas a ir más allá? agrega un nuevo método en el builder que decore el comportamiento del PaymentService usando un decorator visto antes y comparte tu solución en comentarios.