Patrones de Dise帽o y Principios SOLID

1

Patrones de Dise帽o y Principios SOLID en Python

Principios SOLID

2

Principio de Responsabilidad 脷nica en Clases de Software

3

Refactorizaci贸n de C贸digo: Principios SOLID en Procesadores de Pago

4

Refactorizaci贸n: Aplicando el Principio de Responsabilidad 脷nica

5

Principio Open-Closed aplicado en Python

6

Aplicaci贸n del Principio Abierto Cerrado en Python con Pydantic

7

Principio de Sustituci贸n de Liskov en Dise帽o de Software

8

Implementaci贸n del Principio Liskov en Python: Protocolos vs Clases Abstractas

9

Segregaci贸n de Interfaces en Desarrollo de Software

10

Implementaci贸n del Principio de Segregaci贸n de Interfaces

11

Principio de Inversi贸n de Dependencias en C#

12

Principio de Inversi贸n de Dependencias en Python

Reestructuraci贸n del proyecto

13

Reestructuraci贸n de C贸digo con M贸dulos en Python

Patrones de Dise帽o

14

Patrones de Dise帽o: Creaci贸n y Uso Pr谩ctico en Software

15

Uso del Patr贸n Strategy en Procesamiento de Pagos

16

Implementaci贸n del patr贸n estrategia en un procesador de pagos Python

17

Patr贸n Factory Pattern: Creaci贸n de Objetos en Python

18

Patr贸n Factory en Procesadores de Pago con Python

19

Patr贸n Decorador: A帽adir Funcionalidad sin Modificar Estructura

20

Patr贸n Decorador en Procesamiento de Pagos con Python

21

Patr贸n Builder: Separaci贸n de Construcci贸n en Clases Complejas

22

Construcci贸n de Servicio de Pagos con Builder Pattern Python

23

Patr贸n Observer: Gesti贸n de Eventos y Notificaciones en Java

24

Notificaciones en Sistemas de Pago: Implementando el Patr贸n Observer

25

Patr贸n Chain of Responsibility en Sistemas de Pago

26

Patr贸n Chain of Responsibility en Python: Validaci贸n de Pagos

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

Aplicaci贸n del Principio Abierto Cerrado en Python con Pydantic

6/27
Recursos

El principio de abierto-cerrado promueve la creaci贸n de c贸digo que permita extender funcionalidades sin modificar el comportamiento original. A continuaci贸n, veremos c贸mo se puede aplicar este principio en el contexto de un procesador de pagos que debe a帽adir nuevas pasarelas sin cambiar el c贸digo existente.

驴C贸mo se aplica el principio abierto-cerrado en un procesador de pagos?

La clave para implementar el principio abierto-cerrado en un sistema de pagos es dise帽ar el c贸digo de manera que pueda admitir nuevas pasarelas sin modificar la estructura existente. Esto se logra utilizando clases abstractas o interfaces que act煤an como intermediarios. As铆, los procesadores de pagos espec铆ficos, como Stripe, pueden heredar de estas clases abstractas y a帽adir su propia l贸gica sin afectar el c贸digo original.

驴Qu茅 cambios se hicieron para implementar una nueva pasarela?

Para incorporar una nueva pasarela de pagos en este caso:

  • Se cre贸 una nueva carpeta con ejemplos de antes y despu茅s de aplicar el principio abierto-cerrado.
  • Los datos del cliente y el pago se refactorizaron utilizando Pydantic, que es la librer铆a m谩s popular en Python para validaciones de datos.
  • Se introdujeron modelos de datos tipados para CustomerData y PaymentData, con campos claros como montos (enteros) y fuentes de pago (cadenas de texto).

驴C贸mo se manejan los datos con Pydantic?

Pydantic permite definir modelos de datos que facilitan la validaci贸n y tipado. Por ejemplo, el modelo CustomerData contiene atributos como name (nombre del cliente) y contact_info (informaci贸n de contacto), que incluye campos opcionales como tel茅fono o correo electr贸nico. Esto hace que la manipulaci贸n de datos sea m谩s clara y segura.

驴C贸mo se crean nuevas pasarelas de pagos usando clases abstractas?

Primero, se define una clase abstracta que representar谩 un procesador de pagos. Esta clase no contiene l贸gica interna, sino que se utiliza para definir la firma del m茅todo principal, processTransaction. Los procesadores de pagos como Stripe implementan esta clase abstracta, heredando su forma y a帽adiendo la l贸gica espec铆fica para procesar las transacciones.

  • Se define una clase abstracta PaymentProcessor que incluye el m茅todo processTransaction.
  • Stripe, por ejemplo, hereda de esta clase abstracta para implementar su propia l贸gica de procesamiento.
  • El servicio de pagos ya no depende de la implementaci贸n concreta de Stripe, sino que interact煤a con la clase abstracta, lo que facilita a帽adir nuevas pasarelas sin tocar el c贸digo base.

驴C贸mo se manejan las notificaciones de confirmaci贸n?

Se sigui贸 una estrategia similar para las notificaciones. Al igual que con los procesadores de pagos, se cre贸 una clase abstracta Notifier que define la firma del m茅todo SendConfirmation. Esto permite crear diferentes implementaciones, como un notificador por correo electr贸nico o un notificador por SMS, sin afectar la estructura del c贸digo original.

  • Se introdujo un EmailNotifier y un SMSNotifier, ambos heredando de la clase abstracta Notifier.
  • El c贸digo decide din谩micamente qu茅 tipo de notificaci贸n enviar seg煤n los datos del cliente, ya sea por correo o por SMS.

驴C贸mo se extendi贸 el c贸digo sin modificar su estructura original?

Al final, el servicio de pagos se extendi贸 para que permitiera enviar notificaciones v铆a SMS sin cambiar su base de c贸digo. Esto se logr贸 creando una nueva clase SMSNotifier que tambi茅n hereda de Notifier. El c贸digo puede cambiar entre el env铆o de correos o mensajes de texto con solo ajustar la implementaci贸n del servicio, cumpliendo as铆 con el principio abierto-cerrado.

Aportes 13

Preguntas 4

Ordenar por:

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

Porque no hacen esos cambios antes de terminar el video, al menos, a mi me confunde eso cuando debieron hacerlo de antes por eso el archivo es before
el video esta mal editado, repite en el min 5:13 una idea ya expuesta
Para aplicar el \*\*Principio Abierto/Cerrado\*\* (Open/Closed Principle, OCP), necesitamos estructurar el c贸digo de manera que se pueda \*\*extender sin modificar\*\* el c贸digo existente. Esto se puede hacer mediante la creaci贸n de clases abstractas o interfaces que se implementan o heredan en clases espec铆ficas, permitiendo la adici贸n de nuevas funcionalidades sin cambiar la clase base. A continuaci贸n, te muestro un ejemplo y c贸mo aplicarle el OCP. \### Ejemplo de C贸digo que Viola el OCP Supongamos que estamos desarrollando un sistema para calcular el costo de env铆o de un pedido. Inicialmente, solo tenemos el c谩lculo de env铆o est谩ndar, pero m谩s adelante necesitamos agregar otros tipos de env铆o, como env铆o expr茅s y env铆o internacional. Una implementaci贸n que viola el OCP podr铆a verse as铆: ```python class ShippingCalculator: def calculate\_shipping(self, order, shipping\_type): if shipping\_type == "standard": return order.weight \* 5 # Env铆o est谩ndar: $5 por kg elif shipping\_type == "express": return order.weight \* 10 # Env铆o expr茅s: $10 por kg elif shipping\_type == "international": return order.weight \* 20 # Env铆o internacional: $20 por kg else: raise ValueError("Invalid shipping type") ``` Cada vez que se agrega un nuevo tipo de env铆o, hay que modificar la clase `ShippingCalculator`, lo cual viola el principio OCP, ya que no est谩 \*\*cerrada a modificaciones\*\*. \### Soluci贸n: Aplicando el OCP Para cumplir con el principio OCP, podemos crear una clase base o interfaz llamada `ShippingStrategy` y luego extenderla para cada tipo de env铆o, manteniendo el c贸digo de `ShippingCalculator` \*\*cerrado a modificaciones\*\* pero \*\*abierto a la extensi贸n\*\*. ```python from abc import ABC, abstractmethod \# Interfaz o clase abstracta para la estrategia de env铆o class ShippingStrategy(ABC): @abstractmethod def calculate(self, order): pass \# Estrategia de env铆o est谩ndar class StandardShipping(ShippingStrategy): def calculate(self, order): return order.weight \* 5 \# Estrategia de env铆o expr茅s class ExpressShipping(ShippingStrategy): def calculate(self, order): return order.weight \* 10 \# Estrategia de env铆o internacional class InternationalShipping(ShippingStrategy): def calculate(self, order): return order.weight \* 20 \# Clase principal que utiliza la estrategia de env铆o class ShippingCalculator: def \_\_init\_\_(self, strategy: ShippingStrategy): self.strategy = strategy def calculate\_shipping(self, order): return self.strategy.calculate(order) ``` \### Explicaci贸n \- \*\*Clase `ShippingStrategy`\*\*: Es una clase abstracta que define el m茅todo `calculate`. Esta clase sirve como interfaz para las estrategias de env铆o. \- \*\*Clases `StandardShipping`, `ExpressShipping`, `InternationalShipping`\*\*: Implementan `ShippingStrategy` y proporcionan el c谩lculo espec铆fico de cada tipo de env铆o. \- \*\*Clase `ShippingCalculator`\*\*: Utiliza una instancia de `ShippingStrategy` para calcular el costo de env铆o sin importar el tipo. De esta manera, se puede extender `ShippingCalculator` con nuevos tipos de env铆o sin modificar su c贸digo. \### Uso del C贸digo Supongamos que tenemos un objeto `order` con un atributo `weight`. As铆 es como podr铆amos calcular el costo de env铆o usando diferentes estrategias: ```python class Order: def \_\_init\_\_(self, weight): self.weight = weight \# Crear un pedido order = Order(weight=10) # Ejemplo de pedido de 10 kg \# Calcular el costo de env铆o est谩ndar calculator = ShippingCalculator(StandardShipping()) print(f"Env铆o est谩ndar: ${calculator.calculate\_shipping(order)}") \# Calcular el costo de env铆o expr茅s calculator.strategy = ExpressShipping() print(f"Env铆o expr茅s: ${calculator.calculate\_shipping(order)}") \# Calcular el costo de env铆o internacional calculator.strategy = InternationalShipping() print(f"Env铆o internacional: ${calculator.calculate\_shipping(order)}") ``` \### Ventajas de Aplicar el OCP 1\. \*\*Extensibilidad\*\*: Podemos a帽adir nuevos tipos de env铆o (por ejemplo, "Env铆o nocturno") simplemente creando una nueva clase que herede de `ShippingStrategy`, sin necesidad de modificar `ShippingCalculator`. 2\. \*\*Mantenibilidad\*\*: Al no tener que modificar `ShippingCalculator`, se reduce el riesgo de introducir errores en la l贸gica existente. 3\. \*\*Reusabilidad\*\*: Cada estrategia de env铆o es una clase independiente, por lo que puede reutilizarse y probarse de forma aislada. \### Conclusi贸n Al aplicar el \*\*Principio Abierto/Cerrado\*\* hemos hecho que el c贸digo sea m谩s \*\*modular\*\* y f谩cil de \*\*extender\*\*. Esto nos permite agregar funcionalidades nuevas sin necesidad de modificar el c贸digo existente, lo cual es esencial para mantener un sistema robusto y escalable.
No le veo sentido hacer una clase dataclass si no tiene atributos. Respecto a las interfaces tambi茅n se puede usar Protocol, un poco m谩s flexible y nos evita la herencia.
## **馃 Preguntas clave para saber si necesitas OCP** 1. **驴Necesito modificar una clase o funci贸n cada vez que agrego una nueva funcionalidad?** (Si es as铆, aplica OCP). 2. **驴Puedo extender la funcionalidad agregando una nueva clase en lugar de modificar una existente?** 3. **驴Hay muchos if/else o switch/case que podr铆an convertirse en clases separadas?** 4. **驴Cada vez que hay un cambio en las reglas de negocio, tengo que modificar el c贸digo existente?**
Otro ejemplo de uso de OCP Sin OCP: Aqui se viola el principio porque para agregar un nuevo tipo de descuento se tendria que modificar la clase DiscountCalculator ```python class DiscountCalculator: def __init__(self, discount_type, amount): self.discount_type = discount_type self.amount = amount def calculate_discount(self): if self.discount_type == "percentage": return self.amount * 0.10 elif self.discount_type == "fixed": return 20 elif self.discount_type == "seasonal": return self.amount * 0.15 else: return 0 # Uso del calculador de descuentos calculator = DiscountCalculator("seasonal", 100) ```Con OCP: ```python from abc import ABC, abstractmethod class DiscountStrategy(ABC): @abstractmethod def calculate_discount(self, amount): pass class PercentageDiscount(DiscountStrategy): def calculate_discount(self, amount): return amount * 0.10 class FixedDiscount(DiscountStrategy): def calculate_discount(self, amount): return 20 class DiscountCalculator: def __init__(self, strategy: DiscountStrategy, amount): self.strategy = strategy self.amount = amount def calculate_discount(self): return self.strategy.calculate_discount(self.amount) # Uso del calculador de descuentos calculator = DiscountCalculator(SeasonalDiscount(), 100) ```
Bueno estas clases si me tomo mas comprenderlas, aqui mis conclusiones: El OCP se viola cuando tenemos una clase en al cual tenemos una logica que tenga que modificarse cuando queremos agregar una nueva funcionalidad. El ejemplo de la clase no queda claro como se viola el principio porque la clase se llama StripePaymentProcessor, quizas debio llamarse solo PaymentProcesor ya que si queremos implementar otra pasarela tendriamos que modificar esa clase lo cual violaria el principio. Pero aqui podriamos crear otra clase como PayPalPaymentProcessor sin modificar la de StripePaymentProcessor y no estariamos violando el principio.
Son varios los cambios que hizo al c贸digo utilizando Pydantic previos a empezar la clase, me gustar铆a que no hicieran eso antes para evitar confusiones, tuve que ubicar varios de esos cambios para hacerlos porque no los ten铆a, especialmente en las firmas de los m茅todos. No lo digo como algo demasiado malo, solo como un detalle que hace que pauses el video muchas veces y pierdas el foco de lo que est谩s haciendo
Muy bien! estoy aprendiendo cosas nuevas. Gracias gracias.
Estaria bueno que compartan un archivo donde compartan las configuraciones de vscode de cada curso, por ejemplo, de este curso me gusta el theme pero debo buscar manualmente cu谩l es.
Este curso es realmente increible!! Por favor hagan m谩s cursos de este nivel de conocimientos. Felicitaciones!!
Pydantic es una biblioteca de Python que permite la validaci贸n de datos y la creaci贸n de modelos de datos utilizando la programaci贸n orientada a objetos. Facilita la definici贸n de estructuras de datos y asegura que los datos cumplen con un esquema espec铆fico. ### Ejemplo b谩sico: ```python from pydantic import BaseModel class CustomerData(BaseModel): name: str email: str phone: str = None # opcional customer = CustomerData(name="Juan", email="[email protected]") print(customer) ``` Este c贸digo crea un modelo `CustomerData` que valida que `name` y `email` sean cadenas. Al crear una instancia, Pydantic verifica autom谩ticamente que los datos proporcionados sean correctos.
Las interfaces (o en Python, clases abstractas) son fundamentales en este caso porque definen una "regla" o "contrato" que todas las implementaciones deben seguir. En este caso la clase PaymentProcessor establece que cualquier nueva pasarela que se quiera implementar su clase debera tener un metodo process\_transaction