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

Patrón Factory en Procesadores de Pago en Python

18/27
Recursos

¿Cómo implementar el patrón Factory para mejorar la creación de clases?

El patrón Factory es una técnica poderosa en programación que permite encapsular el comportamiento de creación de clases, favoreciendo la reutilización del código y facilitando la mantenibilidad de los programas. Al centralizar la lógica de creación, podemos tomar decisiones más informadas y reducir el acoplamiento en nuestros sistemas. Aquí exploramos cómo implementarlo en Python, usando el ejemplo de un sistema de procesadores de pago.

¿Cómo se estructura el sistema de procesadores de pago?

Para gestionar diferentes tipos de pagos, es crucial contar con una estructura que permita identificar qué procesador utilizar dependiendo de las características del pago, tales como su tipo y moneda. Esto se logra a través de clases y métodos bien definidos.

  • Tipos de pago: Se define un tipo de pago a través de un enum, pudiendo ser offline u online. Este tipo de datos es parte del conjunto PaymentData, crucial para identificar el método de procesamiento adecuado.
  • Procesadores disponibles: Existen tres tipos básicos de procesadores de pago:
    • Procesador local.
    • Procesador offline.
    • Procesador Stripe.
  • Lógica de selección: Dependiendo del tipo de pago y la moneda, se selecciona el procesador adecuado. Por ejemplo, un pago online en dólares selecciona el procesador Stripe, mientras que otras monedas optan por el local.

¿Cómo desarrollar la clase PaymentProcessorFactory?

La clase PaymentProcessorFactory destaca por su capacidad de abstractar la lógica de selección del procesador de pago mediante el uso del operador match de Python, uno de los recientes agregados para simplificar la toma de decisiones.

class PaymentProcessorFactory:

    @staticmethod
    def create_payment_processor(payment_data: PaymentData) -> PaymentProcessorProtocol:
        match payment_data.type:
            case PaymentType.OFFLINE:
                return OfflinePaymentProcessor()
            case PaymentType.ONLINE:
                match payment_data.currency:
                    case "USD":
                        return StripePaymentProcessor()
                    case _:
                        return LocalPaymentProcessor()
            case _:
                raise ValueError("Tipo de pago no soportado")

Características principales:

  • Método estático: Define un método estático CreatePaymentProcessor, que toma como único argumento a PaymentData.
  • Operador match: Facilita la selección del procesador de pago deseado según condiciones predefinidas.
  • Excepciones gestionadas: Cuando un tipo de pago no es reconocido, se lanza una excepción ValueError, asegurando que solo se gestionen tipos de pago válidos.

¿Cómo integrar la fábrica en el servicio de pagos?

El servicio de pagos se puede optimizar para utilizar la clase PaymentProcessorFactory, mejorando así la claridad y la capacidad de mantenimiento del código.

class PaymentService:

    @classmethod
    def create_with_payment_processor(cls, payment_data: PaymentData, **kwargs) -> 'PaymentService':
        try:
            processor = PaymentProcessorFactory.create_payment_processor(payment_data)
            return cls(payment_processor=processor, **kwargs)
        except ValueError as e:
            print("Error creando la clase")
            raise e

Detalles de interés:

  • Class Method: create_with_payment_processor es un método de clase que permite la creación del servicio incluyendo el procesador adecuado según PaymentData.
  • Diccionario de atributos: Utiliza **kwargs para aceptar un conjunto flexible de parámetros adicionales, dispersándolos en la clase instanciada.
  • Manejo de errores: A través del bloque try-except, se captura cualquier ValueError que pueda surgir durante la creación del procesador, brindando feedback inmediato.

Pruebas y depuración del proceso

Para verificar el funcionamiento correcto del sistema, y del patrón Factory en particular, se deben seguir pasos clave como:

  • Datos de pago de ejemplo: Crear una instancia de PaymentData con valores como cantidad, moneda y tipo, permitiendo explorar los diferentes flujos de lógica.
  • Instanciación del servicio: Usar PaymentService.create_with_payment_processor para generar un servicio de pago y analizar el comportamiento esperado.
  • Debugging: Utilizar breakpoints para inspeccionar paso a paso cómo se asignan los procesadores según las características de pago.

Aplicando correctamente el patrón Factory en este escenario, se logra una estructura de código escalable y eficaz, alentando a los desarrolladores a continuar explorando y aplicando patrones de diseño en sus proyectos.

Aportes 5

Preguntas 3

Ordenar por:

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

El \*\*Patrón Factory\*\* es útil para centralizar la creación de objetos, especialmente cuando tienes varias clases que comparten una interfaz común o base y quieres que el cliente no se preocupe por los detalles de su creación. Implementaremos un ejemplo paso a paso para entenderlo. \--- \### Ejemplo: Sistema de Vehículos Queremos implementar un sistema para crear vehículos como autos, motocicletas y camiones. Cada uno tendrá características específicas, pero todos compartirán un comportamiento común: \*\*mostrar su tipo de vehículo\*\*. \--- \### Paso 1: Crear la Interfaz Común Definimos una clase base o interfaz que todas las clases de vehículos implementarán. ```python from abc import ABC, abstractmethod class Vehicle(ABC): @abstractmethod def get\_type(self) -> str: """Devuelve el tipo de vehículo.""" pass ``` \--- \### Paso 2: Implementar Clases Concretas Creamos clases para cada tipo de vehículo. ```python class Car(Vehicle): def get\_type(self) -> str: return "Auto" class Motorcycle(Vehicle): def get\_type(self) -> str: return "Motocicleta" class Truck(Vehicle): def get\_type(self) -> str: return "Camión" ``` \--- \### Paso 3: Crear la Fábrica La fábrica centraliza la lógica para instanciar diferentes tipos de vehículos según sea necesario. ```python class VehicleFactory: @staticmethod def create\_vehicle(vehicle\_type: str) -> Vehicle: if vehicle\_type == "car": return Car() elif vehicle\_type == "motorcycle": return Motorcycle() elif vehicle\_type == "truck": return Truck() else: raise ValueError(f"Tipo de vehículo desconocido: {vehicle\_type}") ``` \--- \### Paso 4: Usar el Patrón Factory Utilizamos la fábrica para crear vehículos de forma sencilla. ```python \# Crear vehículos usando la fábrica vehicle1 = VehicleFactory.create\_vehicle("car") vehicle2 = VehicleFactory.create\_vehicle("motorcycle") vehicle3 = VehicleFactory.create\_vehicle("truck") \# Mostrar sus tipos print(vehicle1.get\_type()) # Output: Auto print(vehicle2.get\_type()) # Output: Motocicleta print(vehicle3.get\_type()) # Output: Camión ``` \--- \### Ventajas del Patrón Factory 1\. \*\*Centralización\*\*: Toda la lógica de creación de objetos está en un solo lugar. 2\. \*\*Extensibilidad\*\*: Agregar un nuevo tipo de vehículo solo requiere crear una nueva clase y modificar la fábrica. 3\. \*\*Separación de Responsabilidades\*\*: El cliente no necesita saber cómo se crean los objetos, solo qué tipo necesita. \--- \### Mejorando con Diccionarios Para evitar múltiples condicionales, podemos usar un diccionario para mapear tipos de vehículos a sus clases concretas. ```python class VehicleFactory: \_vehicle\_classes = { "car": Car, "motorcycle": Motorcycle, "truck": Truck, } @staticmethod def create\_vehicle(vehicle\_type: str) -> Vehicle: vehicle\_class = VehicleFactory.\_vehicle\_classes.get(vehicle\_type) if not vehicle\_class: raise ValueError(f"Tipo de vehículo desconocido: {vehicle\_type}") return vehicle\_class() ``` \--- \### Extensión del Patrón Factory \#### Agregar un Nuevo Vehículo Si deseas agregar, por ejemplo, una bicicleta al sistema: 1\. Crea una nueva clase concreta: ```python class Bicycle(Vehicle): def get\_type(self) -> str: return "Bicicleta" ``` 2\. Agrega su mapeo en el diccionario de la fábrica: ```python \_vehicle\_classes = { "car": Car, "motorcycle": Motorcycle, "truck": Truck, "bicycle": Bicycle, # Nueva clase agregada } ``` \### Conclusión El \*\*Patrón Factory\*\* simplifica la creación de objetos al delegar esta tarea a una clase específica. Es un patrón especialmente útil cuando se espera que el sistema crezca, ya que permite añadir nuevos tipos de objetos sin modificar el código del cliente, promoviendo el \*\*Principio Abierto/Cerrado (OCP)\*\*. Este ejemplo muestra cómo aplicar el patrón de forma sencilla en Python, con la flexibilidad para adaptarlo a proyectos más complejos.
La diferencia principal entre `@staticmethod` y `@classmethod` radica en su propósito y uso: - `@staticmethod`: Define un método que no necesita acceso a la instancia (`self`) ni a la clase (`cls`). Se utiliza para encapsular funciones que no dependen de los atributos de la clase o de la instancia. - `@classmethod`: Define un método que recibe la clase como primer argumento (`cls`). Esto permite realizar operaciones que afectan a la clase en sí misma, como modificar variables de clase o crear nuevas instancias. Ambos métodos no requieren instanciar la clase, pero sirven para diferentes propósitos.
Hola, estoy intentando ejecutar un proyecto desde los recursos que tengo, pero al correr el archivo `main.py`, obtengo el siguiente error en todos los imports: `Exception has occurred: ImportError` `attempted relative import with no known parent package ` `File "/root/Python_SourceCode/Python_examples/Python_Design_Patterns_and_SOLID/src/payment_service/main.py", line 1, in <module>` ` from .loggers import TransactionLogger` `ImportError: attempted relative import with no known parent package` ¿Qué podría estar causando este error y cómo lo puedo solucionar? ¡Gracias de antemano!
Usar `@staticmethod` en Python es útil cuando necesitas definir un método que no requiere acceso a los atributos o métodos de instancia o de clase. Al utilizarlo, estás indicando que este método pertenece a la clase y puede ser llamado sin instanciar un objeto de esa clase. Esto mejora la claridad del código y la organización, ya que separa la lógica que no depende del estado de la instancia. En el contexto del patrón Factory mencionado, un método estático puede encapsular la lógica de creación de instancias sin necesidad de modificar el estado de la clase.
Pregunta, entiendo que tambien es posible llamar al PaymentProcessorFactory desde el main sin la necesidad de crear el classmethod. Cual es la ventaja/desventaja de tener la llamada al factory desde dentro de la clase PaymentService vs llamar al PaymentProcessorFactory antes de instanciar la clase PaymentService y mandar el ProtocolProcessor como paramtro de la clase como se hacia antes? no se si se entendio la pregunta? Gracias.