El diseño orientado a objetos se apoya en cuatro pilares (Booch, 1994): abstracción, encapsulamiento, herencia y polimorfismo. Sobre ellos, dos metas transversales guían la calidad estructural (Stevens, Myers y Constantine, 1974): minimizar el acoplamiento (dependencia entre módulos) y maximizar la cohesión (unidad funcional interna de cada módulo). Complementa esta visión el principio DRY (Hunt y Thomas, 1999): cada pieza de conocimiento debe tener una representación única y autoritativa en el sistema.
El acrónimo SOLID (Martin, 2000) agrupa cinco principios:
Los patrones GRASP (Larman, 2004) son nueve guías para asignar responsabilidades: Experto en Información, Creador, Controlador, Bajo Acoplamiento, Alta Cohesión, Polimorfismo, Fabricación Pura, Indirección y Variaciones Protegidas.
El catálogo de los Gang of Four (Gamma et al., 1994) reúne 23 patrones en tres categorías: 5 creacionales (Abstract Factory, Builder, Factory Method, Prototype y Singleton; Singleton garantiza una sola instancia con acceso global), 7 estructurales (Adapter, Bridge, Composite, Decorator, Facade, Flyweight y Proxy) y 11 de comportamiento (Chain of Responsibility, Command, Interpreter, Iterator, Mediator, Memento, Observer, State, Strategy, Template Method y Visitor).
Para el modelado se emplea UML 2.5.1 (OMG, 2017), que define 14 tipos de diagramas (7 estructurales y 7 de comportamiento). En diagramas de clases conviene distinguir las relaciones: herencia, composición, agregación y dependencia.
En el modelado de datos relacional, el modelo Entidad-Relación fue propuesto por Peter Chen (1976) para el diseño conceptual; la normalización busca eliminar redundancias, y la tercera forma normal (3NF) exige estar en 2NF y no tener dependencias transitivas de atributos no clave respecto de la clave primaria (Codd, 1971). En sistemas NO relacionales se privilegia la escalabilidad y esquemas flexibles. Combinar SOLID, GRASP y patrones GoF favorece el diseño de componentes reutilizables, de bajo acoplamiento y alta cohesión.
1. ¿Qué principio de diseño orientado a objetos establece que una clase debe tener una sola razón para cambiar?
El SRP indica que una clase debe tener una única responsabilidad y, por tanto, una sola razón para cambiar; los demás principios regulan extensión, sustitución de subtipos o dependencia de abstracciones, no el número de responsabilidades. (Martin, R. C. (2002). Agile Software Development, Principles, Patterns, and Practices, cap. 8.)
2. Un desarrollador tiene una clase ReporteVentas que calcula totales de ventas, genera el formato del documento en PDF y además guarda el archivo en una carpeta del servidor. Al modificar el formato de impresión, también se rompe la lógica de cálculo. ¿Qué principio SOLID se está violando?
La clase mezcla tres responsabilidades (cálculo, formato y almacenamiento), por lo que cualquier cambio en una afecta a las otras, lo cual es la violación clásica del SRP. (Martin, R. C. (2002). Agile Software Development, Principles, Patterns, and Practices, cap. 8.)
3. ¿Qué establece el Principio Abierto/Cerrado (OCP) respecto de las entidades de software (clases, módulos, funciones)?
El OCP, formulado por Meyer y reformulado por Martin, exige que el comportamiento se pueda extender sin alterar el código ya probado; la opción sobre subclases corresponde al LSP, no al OCP. (Meyer, B. (1988). Object-Oriented Software Construction; Martin, R. C. (2002).)
4. Un sistema de cálculo de descuentos usa una instrucción switch que evalúa el tipo de cliente (regular, mayorista, gobierno) para aplicar la fórmula correspondiente. Cada vez que se agrega un nuevo tipo de cliente, se debe modificar esa instrucción en varios archivos. ¿Qué solución de diseño resuelve mejor esta violación del OCP?
Extraer una interfaz de estrategia (patrón Strategy) permite agregar nuevos tipos de cliente creando clases nuevas sin modificar el código existente, cumpliendo el OCP. (Meyer, B. (1988). Object-Oriented Software Construction; Martin, R. C. (2002).)
5. Una clase Rectangulo tiene los métodos fijarAncho y fijarAlto, cada uno independiente. La clase Cuadrado hereda de Rectangulo y sobrescribe ambos métodos para que, al fijar un valor, se actualicen ancho y alto por igual. Un programa que usa Rectangulo y espera que el área cambie solo en la dimensión modificada falla al recibir un objeto Cuadrado. ¿Qué principio SOLID se viola?
El ejemplo es el caso clásico del LSP: Cuadrado no puede sustituir a Rectangulo sin alterar el comportamiento esperado por el cliente, lo que rompe la sustituibilidad exigida por Liskov. (Liskov, B. (1987). Data Abstraction and Hierarchy, OOPSLA keynote.)
6. Según el Principio de Inversión de Dependencias (DIP), ¿cómo deben relacionarse los módulos de alto nivel y los de bajo nivel en un sistema?
El DIP establece que tanto los módulos de alto como de bajo nivel deben depender de abstracciones (interfaces), invirtiendo la dependencia tradicional del nivel alto hacia el bajo. (Martin, R. C. (1996). The Dependency Inversion Principle, C++ Report.)
7. Una clase ProcesadorPedidos crea internamente una instancia concreta de BaseDatosMySQL para guardar los pedidos. Si la empresa decide migrar a otro motor de base de datos, hay que modificar ProcesadorPedidos. ¿Qué cambio de diseño aplica el DIP para resolver este problema?
Inyectar una abstracción (RepositorioPedidos) en lugar de instanciar una clase concreta permite cambiar el motor de base de datos sin modificar ProcesadorPedidos, que es la esencia del DIP. (Martin, R. C. (1996). The Dependency Inversion Principle, C++ Report.)
8. ¿Qué establece el Principio de Segregación de Interfaces (ISP)?
El ISP propone dividir interfaces grandes en interfaces más pequeñas y específicas, de modo que cada cliente solo dependa de los métodos que realmente usa. (Martin, R. C. (2002). Agile Software Development, Principles, Patterns, and Practices, cap. 12.)
9. Una interfaz Trabajador define los métodos trabajar y comer. Una clase RobotSoldador implementa Trabajador pero el método comer no tiene sentido para ella, por lo que se implementa lanzando una excepción. ¿Qué solución de diseño corrige esta violación del ISP?
Segregar la interfaz en dos más específicas evita que RobotSoldador se vea obligado a implementar un método que no le corresponde, resolviendo el problema conforme al ISP. (Martin, R. C. (2002). Agile Software Development, Principles, Patterns, and Practices, cap. 12.)
10. ¿Cuántos principios de diseño orientado a objetos agrupa el acrónimo SOLID?
SOLID agrupa cinco principios: SRP, OCP, LSP, ISP y DIP, popularizados por Michael Feathers y Robert C. Martin. (Martin, R. C. (2000). Design Principles and Design Patterns.)
11. ¿Cuál de los siguientes NO es uno de los cinco principios agrupados en el acrónimo SOLID?
DRY (Don't Repeat Yourself) es un principio de diseño distinto, propuesto por Hunt y Thomas, y no forma parte del acrónimo SOLID, el cual incluye SRP, OCP, LSP, ISP y DIP. (Martin, R. C. (2000); Hunt, A., Thomas, D. (1999). The Pragmatic Programmer.)
12. Un equipo de diseño discute la diferencia entre el Principio Abierto/Cerrado y el Principio de Sustitución de Liskov. ¿Cuál es la distinción correcta entre ambos?
El OCP trata sobre extensibilidad sin modificar código probado, y el LSP trata sobre la correcta sustituibilidad de subtipos; son complementarios pero distintos, como los definieron Meyer y Liskov respectivamente. (Meyer, B. (1988); Liskov, B. (1987). OOPSLA keynote.)
13. Según la clasificación del Gang of Four (GoF), ¿cuántos patrones estructurales existen en su catálogo original?
El catálogo del GoF define siete patrones estructurales: Adapter, Bridge, Composite, Decorator, Facade, Flyweight y Proxy. (Gamma et al. (1994). Design Patterns, cap. 4 (Structural Patterns).)
14. ¿Cuál es el propósito principal del patrón Adapter en el catálogo del GoF?
El Adapter permite que clases con interfaces incompatibles trabajen juntas al traducir la interfaz de una clase a la que el cliente necesita; las demás descripciones corresponden a Composite, Decorator y Proxy respectivamente. (Gamma et al. (1994). Design Patterns, cap. 4 (Adapter).)
15. Una empresa integra una librería de pagos de un proveedor externo cuya clase expone el método procesarCobro(monto, moneda), pero el sistema interno de la empresa espera una interfaz con el método pagar(orden). ¿Qué patrón de diseño resuelve esta incompatibilidad sin modificar el código del proveedor externo?
El Adapter envuelve la clase del proveedor externo y traduce las llamadas pagar(orden) hacia procesarCobro(monto, moneda), integrando interfaces incompatibles sin alterar el código original. (Gamma et al. (1994). Design Patterns, cap. 4 (Adapter).)
16. ¿Qué característica define al patrón Decorator dentro de los patrones estructurales del GoF?
El Decorator envuelve un objeto para añadirle funcionalidad adicional en tiempo de ejecución, como alternativa flexible a la herencia; las otras opciones describen Adapter, Flyweight y Bridge. (Gamma et al. (1994). Design Patterns, cap. 4 (Decorator).)
17. En un sistema de manejo de archivos de texto, se requiere poder combinar de forma dinámica funciones como compresión, cifrado y registro de auditoría sobre un flujo de datos base, sin crear una subclase por cada combinación posible. ¿Qué patrón de diseño es más adecuado para este requerimiento?
El Decorator permite envolver el flujo base con varias capas de comportamiento (compresión, cifrado, auditoría) que se pueden combinar dinámicamente, evitando la explosión de subclases que produciría la herencia. (Gamma et al. (1994). Design Patterns, cap. 4 (Decorator).)
18. ¿Qué problema resuelve el patrón Composite en el diseño orientado a objetos?
El Composite organiza objetos en estructuras de árbol parte-todo y permite que el cliente trate a los elementos simples y a las composiciones con la misma interfaz. (Gamma et al. (1994). Design Patterns, cap. 4 (Composite).)
19. Un sistema de administración de archivos debe calcular el tamaño total de una carpeta que puede contener tanto archivos individuales como otras carpetas anidadas, tratando ambos casos con el mismo método obtenerTamano(). ¿Qué patrón de diseño estructural corresponde a este caso?
El Composite modela jerarquías parte-todo (carpetas y archivos) permitiendo invocar el mismo método sobre elementos simples y compuestos, tal como se requiere en este caso. (Gamma et al. (1994). Design Patterns, cap. 4 (Composite).)
20. ¿Cuál es la diferencia esencial entre el patrón Adapter y el patrón Decorator?
El Adapter existe para resolver incompatibilidad de interfaces, mientras que el Decorator preserva la interfaz original del componente y únicamente le añade comportamiento adicional en tiempo de ejecución. (Gamma et al. (1994). Design Patterns, cap. 4.)
21. Un diseñador debe elegir entre Decorator y Composite para modelar un componente gráfico. ¿Qué criterio distingue correctamente cuándo usar cada uno?
Aunque ambos usan composición de objetos con una interfaz común, Composite modela jerarquías parte-todo, mientras que Decorator añade capas de comportamiento a un único componente sin representar una jerarquía estructural. (Gamma et al. (1994). Design Patterns, cap. 4.)
22. ¿Cuál de los siguientes patrones NO pertenece a la categoría de patrones estructurales del catálogo GoF?
Observer es un patrón de comportamiento, mientras que Facade, Bridge y Flyweight sí pertenecen a los siete patrones estructurales definidos por el GoF. (Gamma et al. (1994). Design Patterns, cap. 4 y cap. 5.)
23. En el diseño de componentes reutilizables, ¿qué combinación de características internas y externas se busca maximizar y minimizar, respectivamente, para facilitar su reutilización?
Un componente reutilizable debe tener alta cohesión (unidad funcional interna) y bajo acoplamiento (poca dependencia de otros módulos), lo que facilita su reutilización en distintos contextos. (Stevens, W., Myers, G., Constantine, L. (1974). Structured Design, IBM Systems Journal.)
24. Un componente ValidadorCorreo depende directamente de una clase concreta ConexionServidorSMTP para validar la existencia del dominio, lo que impide reutilizar el componente en proyectos sin ese servidor. ¿Qué acción de diseño mejora la reutilización de este componente?
Separar el componente de la implementación concreta mediante una interfaz y aplicar inyección de dependencias reduce el acoplamiento y permite reutilizar ValidadorCorreo en distintos contextos, incluso sin servidor SMTP. (Stevens, W., Myers, G., Constantine, L. (1974); Martin, R. C. (1996). DIP.)
25. ¿Qué establece el principio DRY (Don't Repeat Yourself) aplicado al diseño de componentes de software?
El DRY, propuesto por Hunt y Thomas, busca evitar la duplicación de conocimiento o lógica en el sistema, concentrándola en una sola representación reutilizable. (Hunt, A., Thomas, D. (1999). The Pragmatic Programmer.)
26. Un equipo diseña un componente CalculadoraImpuestos que debe usarse en varios sistemas de facturación de distintas empresas, cada una con reglas fiscales diferentes. El componente actualmente contiene las reglas de una sola empresa integradas en su código. ¿Qué estrategia de diseño aumenta mejor su reutilización sin generar una copia por cada empresa?
Externalizar las reglas variables mediante una interfaz de estrategia (similar al patrón Strategy) desacopla el componente de una regla fiscal específica, permitiendo su reutilización en múltiples contextos sin duplicar código. (Stevens, W., Myers, G., Constantine, L. (1974); Meyer, B. (1988). OCP aplicado a componentes.)
27. Al diseñar un componente que debe integrarse en múltiples aplicaciones cliente sin conocer sus detalles internos, ¿cuál es la práctica de diseño más recomendada para exponer su funcionalidad?
Exponer una interfaz pública clara y encapsular la implementación permite que los clientes usen el componente sin acoplarse a sus detalles internos, facilitando su reutilización y mantenimiento. (Booch, G. (1994). Object-Oriented Analysis and Design with Applications, 2a ed.; Stevens et al. (1974).)
28. En el patrón GRASP conocido como Experto en Información, ¿a qué clase se le debe asignar una responsabilidad?
El patrón Experto asigna la responsabilidad a la clase que posee la información necesaria para cumplirla, lo que además ayuda a mantener bajo acoplamiento. (Larman, C. (2004). Applying UML and Patterns, 3a ed., cap. 17 (GRASP - Information Expert).)
29. En un sistema de ventas, la clase Pedido contiene una colección de objetos LineaPedido, y cada LineaPedido conoce su cantidad y el precio unitario del producto. Según el patrón Experto en Información, ¿qué clase debe calcular el subtotal de una línea de pedido?
LineaPedido posee los datos (cantidad y precio) requeridos para el cálculo, por lo que el patrón Experto le asigna esa responsabilidad. (Larman, C. (2004). Applying UML and Patterns, 3a ed., cap. 17 (GRASP - Information Expert).)
30. En un sistema bancario, calcular el interés total de una cuenta requiere el saldo (que conoce Cuenta) y la tasa de interés (que conoce TipoDeCuenta); ninguna clase posee ambos datos por sí sola. Según el patrón GRASP Experto en Información, ¿cuál es la solución recomendada?
Cuando la información está distribuida, Experto recurre a un 'experto parcial': la clase que puede colaborar con otra para reunir los datos necesarios, en este caso Cuenta. (Larman, C. (2004). Applying UML and Patterns, 3a ed., cap. 17 (GRASP - Information Expert, experto parcial).)
31. Según el patrón GRASP Creador, la clase B debe encargarse de crear instancias de la clase A si se cumple, entre otras, la condición de que B...
Una de las condiciones del patrón Creador es que B contenga o agregue objetos de A, entre otras como registrar, usar íntimamente o poseer los datos de inicialización de A. (Larman, C. (2004). Applying UML and Patterns, 3a ed., cap. 17 (GRASP - Creator).)
32. En un sistema de reservaciones, la clase Vuelo mantiene una colección de objetos Reservacion y almacena los datos necesarios para inicializarlos (fecha, asiento, pasajero). Según el patrón Creador, ¿qué clase debe crear los objetos Reservacion?
Vuelo cumple varias condiciones del patrón Creador: agrega, contiene y posee los datos de inicialización de Reservacion. (Larman, C. (2004). Applying UML and Patterns, 3a ed., cap. 17 (GRASP - Creator).)
33. En un editor de dibujo vectorial, la clase Lienzo contiene y administra todos los objetos Figura que el usuario dibuja, y almacena los parámetros iniciales (posición, color) de cada figura nueva. Un desarrollador propone que la clase Figura se cree a sí misma porque conoce mejor sus propios atributos. ¿Qué patrón GRASP contradice esa propuesta y por qué?
El patrón Creador asigna la creación a la clase que agrega, contiene y posee los datos de inicialización del objeto creado (Lienzo), no necesariamente a la clase creada. (Larman, C. (2004). Applying UML and Patterns, 3a ed., cap. 17 (GRASP - Creator).)
34. El patrón GRASP Bajo Acoplamiento tiene como objetivo principal...
Bajo Acoplamiento busca minimizar las dependencias entre clases para que un cambio en una tenga menor impacto en las demás. (Larman, C. (2004). Applying UML and Patterns, 3a ed., cap. 17 (GRASP - Low Coupling); Stevens, Myers, Constantine (1974).)
35. Una clase ReporteVentas invoca directamente métodos internos de la clase ConexionBaseDatosMySQL en lugar de usar una interfaz genérica de acceso a datos. Si la empresa migra a otro motor de base de datos, esta decisión obligará a modificar ReporteVentas. ¿Qué patrón de diseño se violó?
Depender directamente de una implementación concreta en lugar de una abstracción incrementa el acoplamiento, contradiciendo el patrón Bajo Acoplamiento. (Larman, C. (2004). Applying UML and Patterns, 3a ed., cap. 17 (GRASP - Low Coupling).)