No tienes acceso a esta clase

隆Contin煤a aprendiendo! 脷nete y comienza a potenciar tu carrera

Aplicar el Principio de Responsabilidad 脷nica (SRP)

4/27
Recursos

Para aplicar el principio de responsabilidad 煤nica (SRP) sobre un procesador de pagos en Python, se debe organizar el c贸digo para que cada clase o m茅todo tenga una 煤nica responsabilidad. A continuaci贸n, te explicar茅 c贸mo refactorizar el c贸digo original, identificando responsabilidades y dividi茅ndolo en componentes m谩s manejables.

驴C贸mo estaba estructurado el c贸digo original?

El c贸digo inicial conten铆a varias responsabilidades en una sola clase llamada PaymentProcessor. Estas responsabilidades inclu铆an:

  • Validaci贸n de datos del cliente
  • Validaci贸n de los datos de pago
  • Procesamiento del pago con Stripe
  • Env铆o de notificaciones (email o SMS)
  • Registro de la transacci贸n en logs

Este enfoque infringe el principio SRP, ya que una sola clase est谩 encargada de m煤ltiples tareas, lo que hace dif铆cil mantener y escalar el c贸digo.

驴C贸mo refactorizar el c贸digo aplicando SRP?

El primer paso fue identificar las distintas responsabilidades dentro de la clase. Se encontraron cuatro bloques importantes de responsabilidades:

  1. Validaci贸n de datos del cliente.
  2. Validaci贸n de datos del pago.
  3. Procesamiento del pago.
  4. Notificaci贸n y logging de transacciones.

驴C贸mo organizar las nuevas clases?

1. 驴C贸mo separar la validaci贸n de datos del cliente?

Se cre贸 una clase CustomerValidation con el m茅todo validate, encargado exclusivamente de validar los datos del cliente, como nombre y contacto. El c贸digo de validaci贸n fue extra铆do de la clase PaymentProcessor y movido aqu铆.

2. 驴C贸mo manejar la validaci贸n del pago?

Al igual que en la validaci贸n del cliente, se cre贸 una clase PaymentDataValidator, encargada de validar los datos de pago, como la fuente de pago (por ejemplo, si se provee una tarjeta o token v谩lido).

3. 驴C贸mo procesar el pago sin romper SRP?

El procesamiento de pago es una responsabilidad que corresponde a la interacci贸n con la API de Stripe. En este caso, se mantuvo esta l贸gica en una clase llamada StripePaymentProcessor, asegurando que solo procese pagos.

4. 驴C贸mo gestionar las notificaciones?

Se cre贸 una clase Notifier, que maneja el env铆o de notificaciones, ya sea por email o SMS. Esto ayuda a aislar la l贸gica de notificaciones del resto del c贸digo, permitiendo cambios futuros sin afectar otras partes.

5. 驴C贸mo registrar logs de transacciones?

Se a帽adi贸 una clase TransactionLogger dedicada al registro de las transacciones. Esta clase se encarga de capturar informaci贸n de la transacci贸n y guardarla en los logs del sistema.

驴C贸mo coordinar todas estas clases?

Se unieron todas estas clases bajo una nueva entidad PaymentService, que orquesta la interacci贸n entre ellas. Esta clase permite coordinar la validaci贸n, procesamiento, notificaciones y registro de transacciones de manera eficiente y con SRP.

驴C贸mo se manejan los errores y excepciones?

Cada clase maneja sus propias excepciones, asegurando que las validaciones levanten errores espec铆ficos cuando los datos son incorrectos. Adem谩s, se agreg贸 manejo de excepciones con try-except para capturar fallas en el procesamiento de pagos, lo que permite gestionar errores de manera controlada.

Aportes 15

Preguntas 1

Ordenar por:

驴Quieres ver m谩s aportes, preguntas y respuestas de la comunidad?

todos habeis olido el codeSmell del minuto 19:00? exacto, est谩 repitiendo exept ValueError as e los try/except a efectos de computo son m谩s costosos que los if, lo ideal en este caso es tener todos los validadores dentro del try y retornar un solo except con el ValueError, porque nos da igual que ValueError sea, el except los captar谩 y retornar谩 independientemente del que sea. no s茅 si lo corregir谩 en videos futuros pero, seguro que m谩s de uno se di贸 cuenta! ese principio no es de solid, pero se llama DRY (dont repeat yourself). excelente video profesor, no hate, nadie es perfecto, solo un peque帽o apunte para ayudar a la comunidad :)
Excelente material! Apenas 4 clases y ya se nota la calidad del curso. En lo personal no me agradan los cursos con clases de m谩s de 10 minutos, pero este fue la excepci贸n. Tan bueno es el material que te engancha, ni si quiera me hab铆a dado cuenta de los m谩s de 20 minutos que contiene esta clase. Bien por Platzi, bien por el profe.
No s茅 que tan de acuerdo est茅n con esto. Al menos esta clase me deja esto: \- Importante diferenciar las responsabilidades ya que en base a esto podemos identificar las clases necesarias. A mi parecer, esto es experiencia y/o mucha pr谩ctica.- Cuando tenemos un c贸digo que tiene una interacci贸n con alguna otra librer铆a y/o aplicaci贸n, debemos seperar este c贸digo a una clase que nos sirva como "puente" de comunicaci贸n con la l贸gica y la librer铆a. Esto con la finalidad de que, si la librer铆a cambia solo debamos modificar en un lugar. A estas clases, generalmente, las nombramos como \_services\_.- Una dataclass permite usar POO de una manera m谩s sencilla y as铆 poder trabajar mejor as铆 como diferenciar responsabilidades y poder aplicar SOLID
Un `dataclass` en Python es una clase que se utiliza principalmente para almacenar datos. Te permite definir clases de manera m谩s sencilla y con menos c贸digo mediante la automatizaci贸n de la creaci贸n de m茅todos como `__init__`, `__repr__`, y `__eq__`. Ejemplo de uso de un `dataclass`: ```python from dataclasses import dataclass @dataclass class Product: name: str price: float quantity: int product = Product(name="Laptop", price=999.99, quantity=10) print(product) # Output: Product(name='Laptop', price=999.99, quantity=10) ``` Este ejemplo muestra c贸mo se define un `dataclass` llamado `Product`, que autom谩ticamente maneja la inicializaci贸n y representaci贸n de la instancia.
Crear clases separadas para validar los datos del cliente y del pago refuerza el principio de responsabilidad 煤nica (SRP) al encapsular la l贸gica espec铆fica de cada responsabilidad en su propia clase. Aunque m茅todos independientes podr铆an parecer suficientes, agrupar funciones relacionadas en clases mejora la cohesi贸n y la mantenibilidad del c贸digo. Esto permite una mejor gesti贸n de los cambios, ya que cada clase se ocupa de su propia l贸gica, facilitando pruebas unitarias y futuras extensiones del sistema sin afectar otras partes del c贸digo.
La clase `CustomerValidator` debe ser utilizada en lugar de una funci贸n para encapsular la l贸gica de validaci贸n y mantener el estado si es necesario. Esto permite aplicar principios de programaci贸n orientada a objetos, como la reutilizaci贸n y la separaci贸n de responsabilidades, lo que es clave en el desarrollo de software escalable y mantenible. Al crear una clase, puedes instanciarla y extender su funcionalidad f谩cilmente, adem谩s de mejorar la organizaci贸n del c贸digo. Esto se alinea con el Principio de Responsabilidad 脷nica, separando la l贸gica de validaci贸n del resto del proceso de pago.
La elecci贸n entre Pydantic y dataclasses depende del contexto de uso. - **Pydantic** es excelente para la validaci贸n de datos y la manipulaci贸n de configuraciones, ya que proporciona caracter铆sticas como la validaci贸n autom谩tica y el soporte para tipos complejos. Es ideal para aplicaciones donde la validaci贸n de datos es cr铆tica, como en APIs. - **Dataclasses** son m谩s simples y livianas, perfectas para estructuras de datos sin validaci贸n adicional. Se utilizan cuando no se necesita la funcionalidad extra de validaci贸n y s贸lo se requiere una representaci贸n eficiente de los datos. Si la validaci贸n es una prioridad, Pydantic es la mejor opci贸n.
No me hace sentido poner esto o sus equivalentes: ```python def process_transaction(self, customer_data, payment_data) -> Charge: try: self.customer_validator.validate(customer_data) self.payment_validator.validate(payment_data) except ValueError as e: raise ValueError("Invalid data:", e) ``` considero que un `try except` se deberia de utilizar cuando uno no tiene el control de las cosas y quiere ponerle control y queres ejecutar algo en funcion del error, en este caso, solo es una validaci贸n y es raro hacer un `raise` del mismo error que ya viene del Validator, un ejemplo mas funcional es como cuando usas un paquete externo (en este caso stripe) y queres regresar un custom error, quitando lo que el propio paquete ya retorna (evitando exponer datos) o en transacciones de DB para hcer un rollback de varias inserciones en varias tables si alguna da error
al inicio del curso cuando el profe dijo que ibamos a crear una pasarela de pagos , me emocion茅 bastante , pero cuando v铆 que ya literalmente todo esta creado y que solo es copiar y pegar , se me quitaron las ganas de seguir viendo este curso
Como recomendaci贸n eviten utilizar variables con nombre como a,e,i,o,u ya que realmente el nombre no representa el valor que almacena correctamente.
Como nota general, no deberiamos tener tantas instrucciones dentro de un try/except, esto permite que si falla una operaci贸n se puede identificar de manera rapida.
Para darle el nombre correcto a las validaciones que devuelven una excepcion y que cortan el flujo eso se llama: Early return. Esto permite mantener el c贸digo limpio y sin tanta indentaci贸n
El m茅todo `customer_data.get("name")` se utiliza para acceder de manera segura al valor del campo "name" dentro del diccionario `customer_data`. Si "name" no existe, `get` devuelve `None` en lugar de generar un error, lo que permite manejar la ausencia de datos sin interrumpir la ejecuci贸n del programa. Esto es especialmente 煤til en validaciones, ya que se puede verificar si el nombre est谩 presente antes de proceder con la l贸gica de procesamiento del pago. Este enfoque concuerda con el principio de responsabilidad 煤nica, al mantener la validaci贸n de datos separada del resto de la l贸gica.
| **Cosa por hacer** | **Descripci贸n** | **Pasos** | |------------------------------|---------------------------------------------------------------|------------------------------------------------------------------------------------------------------------| | **1. Identificar responsabilidades** | Analizar el c贸digo y encontrar responsabilidades de cada clase. | - Revisa el c贸digo existente.
- Enumera las responsabilidades. | | **2. Aplicar SRP** | Asegurarte que cada clase tenga una 煤nica responsabilidad. | - Divide las funcionalidades en clases individuales.
- Aseg煤rate de que cada clase act煤e de manera independiente. | | **3. Simplificar la l贸gica** | Hacer que el c贸digo sea m谩s claro y f谩cil de entender. | - Elimina duplicaciones.
- Utiliza nombres descriptivos para m茅todos y variables. | | **4. Usar patrones de dise帽o**| Implementar patrones que faciliten la escalabilidad. | - Identifica patrones aplicables (ej. Singleton, Factory).
- Implementa el patr贸n en el contexto adecuado. | | **5. Escribir pruebas unitarias** | Asegurarte que el c贸digo funcione como se espera. | - Crea pruebas para cada clase nueva.
- Ejecuta las pruebas y valida que todas pasen. | | **6. Documentar cambios** | Mantener un registro de las modificaciones realizadas. | - Actualiza la documentaci贸n y comentarios.
- Aseg煤rate de que sea comprensible para otros desarrolladores. | | **7. Revisar rendimiento** | Comprobar que no haya degradaci贸n en el rendimiento. | - Realiza pruebas de rendimiento.
- Optimiza donde sea necesario. |
Para aplicar el \*\*Principio de Responsabilidad 脷nica\*\* (SRP), dividimos las funcionalidades de una clase o m贸dulo en diferentes partes, de modo que cada parte tenga \*\*una 煤nica responsabilidad\*\*. Esto permite que cada clase o funci贸n tenga un prop贸sito espec铆fico, lo que facilita la lectura, el mantenimiento y la extensi贸n del c贸digo. \### Ejemplo de Aplicaci贸n de SRP Supongamos que estamos desarrollando una aplicaci贸n para gestionar los pedidos en una tienda en l铆nea. A continuaci贸n, tenemos una clase `Order` que viola el principio SRP. ```python class Order: def \_\_init\_\_(self, order\_id, items): self.order\_id = order\_id self.items = items def calculate\_total(self): total = sum(item\['price'] \* item\['quantity'] for item in self.items) return total def save\_to\_database(self): print(f"Saving order {self.order\_id} to database") def send\_confirmation\_email(self): print(f"Sending confirmation email for order {self.order\_id}") ``` Aqu铆, la clase `Order` tiene \*\*tres responsabilidades\*\*: 1\. \*\*Calcular el total del pedido.\*\* 2\. \*\*Guardar el pedido en la base de datos.\*\* 3\. \*\*Enviar un correo de confirmaci贸n.\*\* Esta clase viola SRP porque mezcla varias funcionalidades, lo cual complica el mantenimiento. Para aplicar SRP, debemos dividir estas responsabilidades en clases o m贸dulos separados. \### Soluci贸n: Aplicando SRP Dividimos las responsabilidades en diferentes clases, de modo que cada clase tenga una sola responsabilidad. ```python class Order: def \_\_init\_\_(self, order\_id, items): self.order\_id = order\_id self.items = items class OrderCalculator: @staticmethod def calculate\_total(order: Order): total = sum(item\['price'] \* item\['quantity'] for item in order.items) return total class OrderRepository: @staticmethod def save(order: Order): print(f"Saving order {order.order\_id} to database") class EmailService: @staticmethod def send\_confirmation\_email(order: Order): print(f"Sending confirmation email for order {order.order\_id}") ``` \### Explicaci贸n \- \*\*Clase `Order`\*\*: Contiene solo los datos del pedido, como el identificador y los art铆culos. \- \*\*Clase `OrderCalculator`\*\*: Su 煤nica responsabilidad es calcular el total del pedido. \- \*\*Clase `OrderRepository`\*\*: Solo se encarga de guardar el pedido en la base de datos. \- \*\*Clase `EmailService`\*\*: Su 煤nica responsabilidad es enviar el correo electr贸nico de confirmaci贸n. \### Uso del C贸digo Ahora, podemos utilizar estas clases de forma modular. Cada clase cumple con una responsabilidad espec铆fica. ```python \# Crear el pedido order = Order(order\_id=123, items=\[ {"name": "Product A", "price": 10, "quantity": 2}, {"name": "Product B", "price": 20, "quantity": 1}, ]) \# Calcular total total = OrderCalculator.calculate\_total(order) print(f"Total: ${total}") \# Guardar pedido en la base de datos OrderRepository.save(order) \# Enviar confirmaci贸n de correo EmailService.send\_confirmation\_email(order) ``` \### Ventajas de Aplicar SRP 1\. \*\*Modularidad\*\*: Al dividir las responsabilidades en varias clases, el c贸digo es m谩s modular y f谩cil de entender. 2\. \*\*Facilidad de Mantenimiento\*\*: Si necesitas cambiar la l贸gica de c谩lculo, guardado o env铆o de correos, puedes hacerlo en la clase correspondiente sin afectar el resto del c贸digo. 3\. \*\*Posibilidad de Reutilizaci贸n\*\*: Las clases `OrderCalculator`, `OrderRepository` y `EmailService` pueden reutilizarse en otros contextos sin necesidad de modificarlas. \### Conclusi贸n Aplicar SRP mejora la organizaci贸n y estructura del c贸digo, reduciendo el acoplamiento y aumentando la cohesi贸n. Esto permite un desarrollo m谩s robusto y adaptable a futuros cambios.