DEV Community

Cover image for DIA 2: PRINCIPIO DE RESPONSABILIDAD UNICA
Sadel Fortunato
Sadel Fortunato

Posted on

DIA 2: PRINCIPIO DE RESPONSABILIDAD UNICA

¿Qué es el Principio de Responsabilidad Única?

"Una clase debe tener una sola razón para cambiar"

-Robert C. Martin.

De la forma sencilla se traduce como: cada clase o modulo debe tener una sola razón de ser u ocuparse de una sola cosa y hacerla bien.

¿Qué significa "una razón para cambiar"?

Una razón para cambiar significa que actores podrían solicitar campos, por ejemplo, Equipo de:

  • Ventas
  • Finanzas
  • Usuarios Finales
  • Equipo de seguridad
  • Etc.

Una analogía del mundo real sería EL RESTAURANTE:

Mesero que:

  • Toma ordenes
  • Sirve comida
  • Cobra
  • Limpia
  • Gestiona inventario

Esto destroza en su totalidad este principio, ya que sin muchas funciones delegas a una sola entidad.

podríamos tener:

  • Mesero: Solo toma órdenes y sirve
  • Chef: Solo cocina
  • Personal de limpieza: Limpia
  • Cajero: solo cobra
  • Gerente: Gestiona Inv.

Esto respeta totalmente el principio ya que cada actor o entidad está cumpliendo una única labor y está enfocada en cumplir bien esa labor

Beneficios:

  • Es más mantenible y gestionable
  • Pequeñas actividades tienen menor margen de error
  • La colaboración distribuida hace más reutilizable una entidad

`class Usuario:
def init(self, nombre, email, contraseña):
self.nombre = nombre
self.email = email
self.contraseña = contraseña

def validar_email(self):
    """Valida formato de email"""
    import re
    pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
    return re.match(pattern, self.email) is not None

def encriptar_contraseña(self):
    """Encripta la contraseña"""
    import hashlib
    return hashlib.sha256(self.contraseña.encode()).hexdigest()

def guardar_en_base_datos(self):
    """Guarda usuario en BD"""
    import sqlite3
    conn = sqlite3.connect('usuarios.db')
    cursor = conn.cursor()
    cursor.execute(
        "INSERT INTO usuarios VALUES (?, ?, ?)",
        (self.nombre, self.email, self.encriptar_contraseña())
    )
    conn.commit()
    conn.close()

def enviar_email_bienvenida(self):
    """Envía email de bienvenida"""
    import smtplib
    msg = f"Bienvenido {self.nombre}!"
    # código para enviar email...
    print(f"Email enviado a {self.email}")

def generar_reporte_pdf(self):
    """Genera reporte PDF del usuario"""
    from reportlab.pdfgen import canvas
    # código para generar PDF...
    print(f"PDF generado para {self.nombre}")`
Enter fullscreen mode Exit fullscreen mode

En este código tenemos la clase usuario que hace distintas actividades o mencionado en la analogía anterior del restaurante tenemos el actor usuario que está ejecutando distintas funciones, al igual que la primera representación del mesero.

Aquí podemos identificar que nuestro mesero (clase usuario)
tiene las siguientes actividades:

  1. Gestionar datos del usuario
  2. Validar datos del usuario
  3. Manejar la contraseña del usuario
  4. Notificaciones
  5. Persistencia o Guardar en base de datos
  6. Reportes

Razones para cambiar (Actores):

  1. Que se cambie el proceso de notificación
  2. Que modifiquemos el proveedor del email
  3. Que los reportes tengan formatos distintos
  4. Que cambiemos la encriptación de la contraseña
  5. Que migremos la base de datos de SQLite a PostgreSQL

`

1. Clase Usuario: SOLO datos y lógica básica

class Usuario:
def init(self, nombre, email, contraseña):
self.nombre = nombre
self.email = email
self.contraseña = contraseña

def get_datos(self):
    return {
        'nombre': self.nombre,
        'email': self.email
    }
Enter fullscreen mode Exit fullscreen mode

2. Validador: SOLO validación

class ValidadorEmail:
@staticmethod
def validar(email):
import re
pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}$'
return re.match(pattern, email) is not None

3. Servicio de Seguridad: SOLO encriptación

class ServicioSeguridad:
@staticmethod
def encriptar_contraseña(contraseña):
import hashlib
return hashlib.sha256(contraseña.encode()).hexdigest()

4. Repositorio: SOLO persistencia

class RepositorioUsuario:
def init(self, conexion_bd):
self.conexion = conexion_bd

def guardar(self, usuario):
    cursor = self.conexion.cursor()
    contraseña_hash = ServicioSeguridad.encriptar_contraseña(usuario.contraseña)
    cursor.execute(
        "INSERT INTO usuarios VALUES (?, ?, ?)",
        (usuario.nombre, usuario.email, contraseña_hash)
    )
    self.conexion.commit()

def buscar_por_email(self, email):
    cursor = self.conexion.cursor()
    cursor.execute("SELECT * FROM usuarios WHERE email = ?", (email,))
    return cursor.fetchone()
Enter fullscreen mode Exit fullscreen mode

5. Servicio de Email: SOLO notificaciones

class ServicioEmail:
def init(self, config_smtp):
self.config = config_smtp

def enviar_bienvenida(self, usuario):
    mensaje = f"¡Bienvenido {usuario.nombre}!"
    # Código real de envío de email
    print(f"Email enviado a {usuario.email}: {mensaje}")
Enter fullscreen mode Exit fullscreen mode

6. Generador de Reportes: SOLO PDFs

class GeneradorReportesPDF:
def generar_reporte_usuario(self, usuario):
# Código para generar PDF
print(f"PDF generado para {usuario.nombre}")
return f"reporte_{usuario.nombre}.pdf"

USO:

def registrar_usuario(nombre, email, contraseña):
# Validar
if not ValidadorEmail.validar(email):
raise ValueError("Email inválido")

# Crear usuario
usuario = Usuario(nombre, email, contraseña)

# Guardar
import sqlite3
conn = sqlite3.connect('usuarios.db')
repo = RepositorioUsuario(conn)
repo.guardar(usuario)

# Notificar
servicio_email = ServicioEmail(config_smtp={})
servicio_email.enviar_bienvenida(usuario)

# Generar reporte
generador = GeneradorReportesPDF()
generador.generar_reporte_usuario(usuario)

conn.close()
return usuario
Enter fullscreen mode Exit fullscreen mode

`
BENEFICIOS DE LA REFACTORIZACIÓN:

  1. Usuarios: Solo se encarga de gestionar lo mínimo del usuario
  2. ValidadorEmail: puedes reutilizarse en otros contextos
  3. ServicioSeguridad:Fácil cambiar de SHA256 a bcrypt
  4. El repositorio de usuario es fácil moverlo de DB
  5. La reporteria puede ser reutilizable y flexible para cambiar de libreria
  6. Facil de cambiar el proveedor de email

Ahora cada clase tiene una sola razon para cambiar

Top comments (0)