Curso de Fundamentos de Arquitectura de Software

Paradigmas y principios SOLID explicados

Curso de Fundamentos de Arquitectura de Software

Contenido del curso

Paradigmas y principios SOLID explicados

Resumen

La parte más difícil del trabajo de un arquitecto de software no es diseñar soluciones, sino saber cuándo intervenir para ayudar al equipo con problemas reales en el código. Aquí entran los principios SOLID y los paradigmas de programación, herramientas que te dan criterio para tomar decisiones de diseño sin sobreingeniería.

Si estás aprendiendo arquitectura de software, conviene que entiendas algo desde el inicio: un arquitecto tiene que saber codificar. Los principios son guías, no reglas rígidas, y se aplican tanto en diseños de alto nivel como cuando escribes código línea por línea.

¿Cuáles son los paradigmas base que sostienen SOLID?

Antes de SOLID, necesitas dominar tres paradigmas que funcionan como ladrillos fundamentales de cualquier programa imperativo.

¿Qué es la programación estructurada y por qué importa?

La programación estructurada nace de la necesidad de partir un programa en piezas más pequeñas y especializadas, llamadas funciones [01:45]. Antes existía una sola pila de ejecución donde todo el código se mezclaba. Hoy un programa principal cede el control a funciones específicas y luego lo recupera. Es la base del divide y vencerás.

¿Qué aporta la programación orientada a objetos?

La programación orientada a objetos sube el nivel: permite que el control se pase a alternativas que cumplen un mismo contrato. Eso es polimorfismo, varias formas de resolver un mismo problema bajo una interfaz común [02:20].

¿Y la programación funcional?

La programación funcional restringe los cambios sobre las variables durante la ejecución. La disciplina es de asignación única: las cosas son como se definen, no mutan. El resultado es código más limpio, más fácil de testear y, eso sí, una forma distinta de pensar tus algoritmos [03:00].

¿Qué significa SOLID en programación? Es un acrónimo que reúne cinco principios de diseño: responsabilidad única, abierto/cerrado, sustitución de Liskov, segregación de interfaces e inversión de dependencias.

¿Cómo se aplica el principio de responsabilidad única?

Este principio suele malinterpretarse. No dice que un componente haga una sola cosa, sino que debe tener una sola razón para cambiar [04:10].

Piensa en una clase Empleado de la que dependen tres actores distintos: reglas de negocio, interfaz de usuario y persistencia. Cada actor tiene un ritmo de cambio diferente, y mantener todo en una sola clase obliga a modificarla constantemente. La solución es dividirla en tres, atacando cada necesidad por separado.

  • Reglas de negocio cambian cuando cambia el espacio del problema.
  • La interfaz de usuario cambia por necesidades de comunicación con usuarios.
  • La persistencia cambia por motivos técnicos de almacenamiento.

¿Cómo entender el principio de apertura y cierre con un ejemplo?

Un sistema debe estar abierto para extensión, pero cerrado para modificación. Imagina una CalculadoraGeometrica que depende de figuras concretas para calcular área y perímetro. Cada vez que añades una figura nueva, tienes que modificar la calculadora. Eso es un error de diseño.

La alternativa es delegar el cálculo a cada figura. La calculadora sigue dependiendo del contrato (calcular área, calcular perímetro), pero cada figura, como el círculo, define su propia implementación. Así puedes agregar figuras nuevas sin tocar la calculadora.

Para aplicarlo bien, te conviene:

  • Entender las dependencias entre clases, paquetes y módulos.
  • Hacer test de integración que validen que cambiar tipos derivados no rompe el comportamiento.
  • Detectar dónde dependes del valor de un tipo en lugar de su estructura.

¿Qué resuelve el principio de sustitución de Liskov?

Formulado en 1987 por Barbara Liskov, este principio reduce la ambigüedad en la programación orientada a objetos [07:30]. La idea: un subtipo debe poder reemplazar a su tipo base sin romper el contrato.

El ejemplo clásico son las aves. Si defines que toda ave vuela y nada, te topas con problemas: el pingüino no vuela, el loro no nada. Sin Liskov, terminas usando trucos como excepciones o retornos nulos para tapar esos huecos.

La solución es redefinir el contrato en tipos más específicos: aves voladoras, aves nadadoras, y aves que cumplen ambos contratos como el pato. Cada tipo cambia el contrato sin redefinirlo de forma inconsistente.

¿Cuándo se rompe el principio de Liskov? Cuando una subclase tira excepciones, devuelve null o ignora métodos heredados porque no puede cumplirlos.

¿Cómo funcionan la segregación de interfaces y la inversión de dependencias?

Estos dos principios trabajan juntos para que tu código no quede atado a implementaciones concretas que no necesitas.

¿Qué es la segregación de interfaces?

La segregación por interfaces dice que debes depender de contratos abstractos, no de implementaciones concretas que arrastran código que no usas [09:45]. Si tu app usa un ORM solo en su versión asíncrona, no deberías arrastrar la implementación síncrona, porque un bug ahí también te afecta.

A nivel de diseño, este principio aparece cuando decides depender de estándares o protocolos en lugar de productos:

  • Depender de SQL 2019 en lugar de PostgreSQL.
  • Depender de AMQP en lugar de RabbitMQ.
  • Extraer contratos como interfaces y que las clases concretas las implementen.

Esto es clave cuando despliegas en entornos heterogéneos o necesitas intercambiar herramientas sin cambiar comportamiento.

¿Por qué invertir la dirección de las dependencias?

La inversión de dependencias parece contraintuitiva, pero tiene una razón potente: el cliente siempre tiene la razón. Los contratos se definen en el módulo que los usa, no en el que los implementa [11:20].

Eso te permite que un módulo de alto nivel no dependa de un módulo concreto de bajo nivel. Los clientes definen el contrato y pueden buscar múltiples contratistas, aplicando polimorfismo para resolver el mismo problema con distintas implementaciones.

Todos estos principios actúan a nivel micro. Cuando los aplicas a escala macro, llegas al concepto de arquitectura limpia. ¿Cuál de estos principios sientes que más te cuesta aplicar en tu código actual? Cuéntalo en los comentarios.