Tema 11: Diseño a Nivel de Componentes y Principios de Diseño
1. Resumen Ejecutivo
Esta sesión marca la transición del Dominio del Problema (Análisis/¿Qué?) al Dominio de la Solución (Diseño/¿Cómo?). Se profundiza en la estructura piramidal del diseño, diferenciando entre subsistemas lógicos y componentes físicos de implementación. El núcleo de la clase se centra en los principios fundamentales (SOLID y Cohesión de Paquetes) necesarios para crear software mantenible, modular y reutilizable.
2. Conceptos Clave
-
Dominio de la Solución: Fase donde se define cómo se resolverá el problema tecnológicamente.
-
Alta Cohesión: Agrupación de elementos con un objetivo funcional común y único.
-
Bajo Acoplamiento: Mínima dependencia e interacción necesaria entre componentes.
-
Interfaz: Mecanismo público que permite la comunicación entre componentes ocultando su implementación interna.
-
Componente: Elemento técnico a bajo nivel (ej. un archivo
.jar) que implementa la funcionalidad.
3. Desarrollo del Temario
3.1. El Proceso de Diseño: Del Análisis a la Implementación
El diseño es el puente entre los requisitos y el código final. No es un paso único, sino un proceso iterativo.
-
Dominio del Problema (¿Qué?): Corresponde a la toma de requisitos y el análisis. Es entender la necesidad del cliente.
-
Dominio de la Solución (¿Cómo?): Es la búsqueda de alternativas tecnológicas y la elección de la más óptima para la implementación.
La Pirámide del Diseño El diseño se construye por capas, donde la base es fundamental para las siguientes:
-
Diseño de Datos/Clases: La base. Estructura de la información requerida.
-
Diseño de la Arquitectura: Relación entre los elementos principales (subsistemas) y patrones arquitectónicos.
-
Diseño de la Interfaz: Cómo se comunica el software con sistemas externos y usuarios.
-
Diseño a Nivel de Componentes: Transforma elementos estructurales en una descripción procedimental.
Analogía del Profesor: El diseño arquitectónico (subsistemas) es como el plano de una vivienda; define cómo será pero no es "real" todavía. El diseño de componentes y despliegue es cuando construimos "el túnel" (implementación real, ej. un
.jar) y decidimos dónde colocarlo (nodos computacionales). [813s - 887s]
3.2. Conceptos Fundamentales de Diseño
Para diseñar correctamente, debemos aplicar ciertos criterios de calidad:
-
Abstracción y Refinamiento: Proceso de descender desde una visión general hasta el detalle técnico.
-
Modularidad: División del sistema en partes manejables (Dividir y Vencerás).
-
Ocultamiento de la Información:
-
Los componentes exponen Interfaces Públicas (servicios que ofrecen).
-
Mantienen su Funcionamiento Interno Oculto.
-
Ejemplo: Una calculadora. Sabes que si pulsas
logobtienes el logaritmo (interfaz), pero no necesitas saber el algoritmo matemático interno que lo calcula. [1723s] -
Independencia Funcional: El "Santo Grial" del diseño: Alta Cohesión y Bajo Acoplamiento.
3.3. Pasos del Diseño (Proceso Sugerido)
Aunque el diseño es creativo e iterativo, se sugiere un flujo lógico:
-
Modelar clases del dominio del problema: (Datos conceptuales).
-
Modelar clases del dominio de la solución: (Clases reales/técnicas).
-
Reutilización: Identificar qué no necesitamos construir desde cero (librerías, componentes existentes).
-
Persistencia: Definir cómo se almacenan los datos (Bases de datos).
-
Desarrollo del comportamiento: Diagramas de secuencia, actividad, estados.
-
Modelado de despliegue: Dónde se ejecutan los componentes.
-
Alternativas: Ofrecer varias soluciones y elegir la mejor.
3.4. Principios de Diseño (SOLID y Cohesión)
¡OJO AL DATO! Estos principios son fundamentales para exámenes y práctica profesional. Son las "reglas de oro" para evitar un mal diseño.
A. Principio Abierto-Cerrado (Open-Closed Principle - OCP)
-
Definición: Un componente debe ser abierto para su extensión pero cerrado para su modificación.
-
Objetivo: Poder añadir nueva funcionalidad sin "romper" o tocar el código existente.
-
Mecanismo: Uso de interfaces y polimorfismo (Realización).
-
Ejemplo de Clase: Sistema de alarmas. Tienes
SensorPuertaySensorHumo. Si quieres añadirSensorCO2, no deberías modificar el código delDetector. Si todos implementan la interfazSensor(con métodosleer(),activar()), el detector funciona con el nuevo sensor sin cambios. [2616s - 2768s]
B. Principio de Sustitución de Liskov (LSP)
-
Definición: Toda subclase debe poder sustituir a su clase base sin alterar el correcto funcionamiento del programa.
-
Contexto: Herencia.
-
Ejemplo de Clase: Clase
Empleadocon subclassesGerente,Dependiente,Supervisor. El métodocalcularSueldo(Empleado e)debe funcionar pasándole cualquiera de los tres. Si al pasar unGerenteel sistema falla o requiere unif(esGerente)especial fuera del polimorfismo, violamos el principio. [2836s - 2913s]
C. Principio de Inversión de Dependencia (DIP)
-
Definición: Un componente debe depender de abstracciones (interfaces), no de concreciones (clases específicas).
-
Aplicación: No hacer llamadas a métodos concretos directamente.
-
Ejemplo de Clase: Un
Botónno debe depender directamente de una claseLámpara. Si mañana quieres que el botón encienda unTelevisor, tendrías que cambiar el código del botón. Solución: El botón depende de una interfazConmutable(conencender()/apagar()). Tanto la lámpara como la tele implementanConmutable. [3005s - 3157s]
D. Principio de Segregación de la Interfaz (ISP)
-
Definición: Es mejor tener muchas interfaces específicas que una sola de propósito general.
-
Beneficio: Favorece la independencia funcional. Los clientes no deben depender de métodos que no usan.
-
Ejemplo de Clase: Si tienes una
BigInterfacecon métodos de Pedidos y Almacén, un cliente que solo hace Pedidos se ve obligado a conocer métodos de Almacén. Es mejor dividirla enInterfazPedidoseInterfazAlmacen. [3246s - 3322s]
E. Principios de Cohesión (Nivel de Paquete/Componente)
Reglas sobre cómo agrupar clases en paquetes o librerías.
- REP (Reuse-Release Equivalence Principle):
-
Equivalencia Reutilización-Liberación. El gránulo de reutilización es el gránulo de la liberación (release).
-
Lo que ofreces para reutilizar (ej. una librería) debe ser lo que liberas en una versión estable. No puedes liberar algo a medias si esperas que se reutilice. [3538s].
-
CCP (Common-Closure Principle):
-
Cierre Común. Las clases que cambian juntas, deben agruparse juntas.
-
Si al modificar la clase
Pedidosiempre tienes que modificar la claseLineaPedido, ponlas en el mismo paquete. Si están separadas, rompes el encapsulamiento al tener que tocar múltiples paquetes por un solo cambio lógico. [3717s]. -
CRP (Common-Reuse Principle):
-
Reutilización Común. Las clases que no se reutilizan juntas no deben agruparse juntas.
-
Si para usar la clase A tengo que importar un paquete que trae la clase B (que no necesito), estoy generando una dependencia inútil. "Si reutilizas una clase, tiene sentido que reutilices todo el componente".
4. Preguntas de Autoevaluación
- ¿Cuál es la diferencia principal entre el Principio de Inversión de Dependencia y una dependencia tradicional?
-
Respuesta: En una dependencia tradicional, el alto nivel depende del bajo nivel (concreción). En la inversión, ambos dependen de una abstracción (interfaz), permitiendo cambiar la implementación de bajo nivel sin afectar al alto nivel.
-
Si tengo un paquete gigante "Utils" donde guardo clases de matemáticas, clases de acceso a base de datos y clases de interfaz gráfica, ¿Qué principio de cohesión estoy violando probablemente?
-
Respuesta: El CRP (Common-Reuse Principle) y probablemente el CCP. Estoy obligando a un usuario que solo quiere matemáticas a depender de librerías gráficas. Además, tienen baja cohesión.
-
Explica con tus palabras la diferencia entre Diseño de Arquitectura y Diseño de Componentes según la clase.
-
Respuesta: El diseño de arquitectura (subsistemas) es el "plano" lógico y estructural de alto nivel. El diseño de componentes es la especificación técnica de bajo nivel (ej. archivos .jar, dll) lista para ser implementada y desplegada en nodos físicos.
-
Según el Principio Abierto-Cerrado, ¿qué parte debe estar "cerrada" y qué parte "abierta"?
-
Respuesta: El componente debe estar abierto a la extensión (nuevas funcionalidades) pero cerrado a la modificación (no tocar el código fuente original para añadir esa funcionalidad).
-
¿En qué fase de la "Pirámide de diseño" se definen las estructuras de datos que implementan el software?
- Respuesta: En el Diseño de datos o clases (la base de la pirámide), donde se transforman los modelos de dominio en estructuras de datos concretas para la implementación.