Ingeniería de Software 2015
3.1 Descomposición Modular:
Técnicas Generales de Diseño de Software El diseño de software es una actividad que requiere tener cierta experiencia previa. En este tema se hace un repaso a las técnicas más importantes de diseño que se emplean habitualmente. Con carácter general, todas las técnicas tratan de conseguir como objetivos inmediatos del diseño los siguientes: • La descomposición modular del sistema. Aplicar el concepto de modularidad y obtener una división del sistema en partes o módulos. • Decidir sobre los algoritmos y/o las estructuras de datos fundamentales que se deben utilizar para la realización del sistema A continuación se estudian las técnicas de diseño propiamente dichas, agrupadas en: 1. Diseño modular descendente 2. Diseño orientado a objetos 3. Diseño de datos DESCOMPOSICIÓN MODULAR Todas las técnicas de diseño están de acuerdo en la necesidad de realizar una descomposición modular del sistema como actividad fundamental del diseño y para lograrlo es necesario concretar los siguientes aspectos: • Identificar los módulos • Describir cada módulo • Describir las relaciones entre módulos La diferencia fundamental entre las distintas técnicas de diseño es precisamente lo que se entiende por módulo en cada una de ellas y los criterios que se deben emplear para su identificación. Con carácter muy general, se puede decir que un módulo es un fragmento de un sistema software que se puede elaborar con relativa independencia de los demás. La idea básica es precisamente que la elaboración de los diferentes módulos se pueda encargar a personas distintas y que todas ellas trabajen en paralelo. Los tipos de módulos son innumerables, algunos de ellos pueden ser los siguientes: • Código fuente.- Contiene texto fuente escrito en el lenguaje de programación elegido. Es el tipo de módulo que se usa con mayor frecuencia. Su formato y organización dependen mucho de la técnica y el lenguaje empleado. • Tabla de Datos.- Se utiliza para tabular ciertos daos de inicialización, experimentales o simplemente de difícil obtención. • Configuración.- Un sistema se puede concebir para trabajar con entornos diversos según las necesidades de cada cliente. En estos casos, interesa agrupar en un mismo módulo toda aquella información que permite configurar el entorno concreto de trabajo. • Otros.- En general, un módulo puede servir para agrupar ciertos elementos del sistema relacionados entre sí y que se puedan tratar de forma separada del resto. También se elaboran por separado los ficheros de ayuda en línea, los manuales de , etc.
Ingeniería de Software 2015 El formato concreto de cada tipo de módulo depende de la técnica, metodología o herramienta utilizados. Normalmente el objetivo fundamental de cualquier diseño es conseguir un sistema mantenible y solo en casos excepcionales se sacrificará este objetivo para lograr una mayor velocidad de proceso o un menor tamaño de código. No obstante, la experiencia acumulada permite establecer que una descomposición modular debe poseer ciertas cualidades mínimas para que se pueda considerar suficientemente válida. Estas cualidades son las que veremos a continuación. Independencia Funcional En la matriz requisitos/componentes del final de los documentos ADD y DDD es necesario indicar qué componente (módulo) se encargará de realizar cada uno de los requisitos (funciones) indicados en el documento SRD. Como primer paso de la descomposición se puede establecer que cada función se podrá realizar en un módulo distinto. Si el análisis está bien hecho y las funciones son independientes, estos módulos tendrán independencia funcional entre ellos. Posteriormente y en sucesivos pasos de refinamiento del diseño se agruparán funciones afines en un mismo módulo o se subdividirán ciertos módulos en otros varios más sencillos. Para que un módulo posea independencia funcional debe realizar una función concreta o un conjunto de funciones afines, sin apenas ninguna relación con el resto de los módulos del sistema. Una mayor independencia redunda en una mayor facilidad de mantenimiento o sustitución de un módulo por otro y aumenta la posibilidad de reutilización del módulo. Para medir de una forma relativa la independencia funcional que hay entre varios módulos se utilizan fundamentalmente dos criterios: acoplamiento y cohesión. 1. Acoplamiento El grado de acoplamiento entre módulos es una medida de la interrelación que existe entre ellos: tipo de conexión y complejidad de la interfase. Para medir de una forma cualitativa el grado de acoplamiento entre módulos se utiliza la siguiente escala:
El objetivo que se persigue es que durante el diseño, el acoplamiento sea mínimo por lo que habrá que buscar descomposiciones con acoplamiento débil o como mucho moderado. Acoplamiento por Contenido: Se produce cuando desde un módulo se pueden cambiar los datos locales e incluso el código de otro módulo. Este tipo de acoplamiento sólo se puede lograr utilizando un lenguaje ensamblador o de muy bajo nivel y puede y debe ser evitado siempre. Acoplamiento Común: Se emplea una zona común de datos a la que tienen varios o todos los módulos del sistema. La depuración y el mantenimiento de un sistema con esta descomposición, al igual que el anterior, resulta muy difícil y siempre que se puede se debe evitar. Acoplamiento Externo: Está constituida por algún dispositivo externo (disco, sensor, etc.) al que están ligados todos los módulos. La estructura de la zona común la impone el formato de los datos que maneja el dispositivo y cualquier modificación exige el cambio de todos los módulos.
Ingeniería de Software 2015 Acoplamiento de Control o acoplamiento moderado: En este caso, una señal (s) o dato de control que se pasa de un módulo (A) a otro (B) es lo que determina la línea de ejecución que se debe seguir dentro de este último (B).
Acoplamient o Débil : Se produce sólo a través del intercambio de aquellos datos que un módulo necesita de otro. Si el intercambio se realiza estrictamente con los únicos datos que se necesitan tenemos un acoplamiento de Datos. Este es el mejor tipo posible de acoplamiento. Cuando en el intercambio se suministra una referencia que facilita el no sólo a los datos estrictamente necesarios sino también a la estructura completa (pila, vector, árbol, grafo, etc.) tenemos un acoplamiento por Etiqueta. Ambos tipos de acoplamiento débil son los más deseables en una descomposición modular. Evidentemente el acoplamiento más débil es el que no existe. Este caso es el que se produce entre los módulos E y B, entre los que no existe ningún tipo de acoplamiento directo. 2. Cohesión El criterio de cohesión es complementario al de acoplamiento. Además de buscar un acoplamiento débil entre módulos es necesario lograr que el contenido de cada módulo tenga coherencia. Cuando se descompone un sistema se debe buscar un objetivo específico para cada módulo. A continuación, se tratará de agrupar en el mismo módulo todos aquellos elementos afines o que estén relacionados con el objetivo fijado para dicho módulo. Como escala para medir de forma cualitativa la cohesión de un módulo se utiliza la siguiente:
Cohesión Coincidental: Es la peor posible y se produce cuando cualquier relación entre los elementos del módulo es una “pura coincidencia”, es decir, no guardan absolutamente ninguna relación entre ellos. Cohesión Lógica: Se produce cuando se agrupan en un mismo módulo elementos que realizan funciones similares desde un punto de vista de . Esta misma cohesión es la que existe en los módulos de entrada/ salida o cuando se diseña un módulo para el manejo de todos los mensajes de error que se producen en el sistema. Cohesión temporal: Es el resultado de agrupar en un mismo módulo aquellos elementos que se ejecutarán en un mismo momento. Esta es la situación que se produce en la fase de inicialización o finalización del sistema en que necesariamente se deben “arrancar” o “parar” dispositivos completamente heterogéneos: teclado, pantalla, ratón, impresora, etc.
Ingeniería de Software 2015 La cohesión BAJA debe evitarse prácticamente siempre. De hecho, tan solo se podría justificar una cohesión lógica o temporal y solamente en los casos dados como ejemplo u otros semejantes. Cohesión de Comunicación: Es aquella que se produce cuando todos los elementos el módulo operan con el mismo conjunto de datos de entrada o producen el mismo conjunto de datos de salida. Cohesión Secuencial: Se produce cuando todos los elementos del módulo trabajan de forma secuencial. Esto es, la salida de un elemento del módulo es la entrada del siguiente de una manera sucesiva. Con una cohesión MEDIA se puede reducir el número de módulos. Sin embargo, esto no se debe realizar a costa de aumentar el grado de acoplamiento entre módulos. Cohesión Funcional: Cada elemento está encargado de la realización de una función concreta y específica. Cohesión Abstraccional: Con la técnica de diseño basado en abstracciones, un módulo con cohesión funcional sería una abstracción funcional. Esta cohesión se logra cuando se diseña un módulo como tipo abstracto de datos con la técnica basada en abstracciones o como una clase de objetos con la técnica orientada a objetos. En ambos casos, se asocian un cierto contenido (atributos) u organización de datos con las correspondientes operaciones (métodos) encargados de su manejo. Independientemente de la técnica empleada, una cohesión ALTA debe ser el objetivo que se debe perseguir en cualquier descomposición modular. Con ello, se facilitará en gran medida el mantenimiento y la posible reutilización de los módulos así diseñados. En resumen, la descomposición modular con una mayor independencia funcional se logra con un acoplamiento DÉBIL entre sus módulos y una cohesión ALTA dentro de cada uno de ellos. Comprensibilidad La dinámica del proceso de diseño e implementación de un sistema hace que los cambios sean más frecuentes de lo que sería deseable. Posteriormente, los cambios continúan durante la fase de mantenimiento hasta que se sustituye el sistema por otro nuevo. Para facilitar e incluso posibilitar estos cambios es necesario que cada módulo sea comprensible de forma aislada. El primer factor que facilita la comprensión de un módulo es su independencia funcional. Como se ha visto antes, con una cohesión alta y un acoplamiento débil, el módulo tiene menor dependencia del resto del sistema y por tanto será más fácil entender su funcionamiento de manera aislada. Pero además es necesario cuidar los siguientes factores: 1. Identificación: Elegir adecuadamente el nombre del módulo y de los nombres de cada uno de sus elementos. Los nombres deben reflejar de manera sencilla el objetivo de la entidad que representan. 2. Documentación: La labor de documentación de cada módulo y del sistema en general debe servir para facilitar la comprensión. Se deben establecer normas y convenios de documentación que evitar problemas de comprensión. Estas normas formarán parte del Documento de Diseño Detallado (DDD). 3. Simplicidad: Las soluciones sencillas son siempre las mejores. Un esfuerzo fundamental del diseñador debe estar dedicado a simplificar al máximo las soluciones propuestas.
Ingeniería de Software 2015 Adaptabilidad La independencia funcional y la comprensibilidad son dos cualidades esenciales que debe tener cualquier diseño para posibilitar su adaptabilidad. Otros factores adicionales para facilitar la adaptabilidad son los siguientes: 1. Previsión: Resulta muy complicado prever qué evolución futura tendrá un determinado sistema. Solo la experiencia previa nos podría indicar qué partes del sistema han sido más dados a cambios o adaptaciones en otros sistemas semejantes. 2. Accesibilidad: Antes de abordar la nueva adaptación de un sistema, es necesario conocerlo con la suficiente profundidad. Este trabajo sólo es posible si resulta sencilla la accesibilidad a todos los documentos de especificación, diseño e implementación. 3. Consistencia: Cuando se realizan adaptaciones sucesivas, es vital mantener la consistencia entre todos los documentos de especificación, diseño e implementación para cada nueva adaptación. Existen herramientas para el “control de versiones y configuración” que permiten mantener automáticamente la consistencia en cada adaptación. TECNICAS DE DISEÑO FUNCIONAL DESCENDENTE En este grupo de técnicas se incluyen todas aquellas en que la descomposición del sistema se hace desde un punto de vista funcional, es decir, se atiende fundamentalmente a la función o funciones que ha de realizar el sistema, que se van expresando poco a poco mediante funciones más sencillas, las cuales se encomiendan a módulos separados. Desde el punto de vista de la codificación, cada módulo corresponde esencialmente a un subprograma. Por esta razón estas técnicas de diseño conducen a estructuras modulares que pueden implementarse bien casi con cualquier lenguaje de programación. Desarrollo por refinamiento progresivo Esta técnica corresponde a la aplicación en la fase de diseño de la metodología de programación conocida como programación estructurada, y que condujo a la construcción de programas mediante refinamientos sucesivos. La programación estructurada recomienda emplear en la construcción de programas sólo estructuras de control claras y sencillas, con un único punto inicial y un único punto final de ejecución. En particular son la secuencia, la selección entre alternativas, y la iteración. El concepto de refinamiento consiste en plantear inicialmente el programa como una operación global, única, e irla descomponiendo poco a poco en función de otras operaciones más sencillas. Ejemplo de refinamientos sucesivos:
Programación estructurada de Jackson Esta técnica sigue las ideas de la programación estructurada en cuanto a las estructuras recomendadas (secuencia, selección e iteración) y el método de refinamientos sucesivos para construir la estructura del programa en forma descendente. La técnica original de la programación estructurada de Jackson se basa en los siguientes pasos: 1.
Analizar el entorno del problema y describir las estructuras de datos a procesar.
2. Construir la estructura del programa basada en las estructuras de datos. 3. Definir las tareas a realizar en términos de las operaciones elementales disponibles, y situarlas en los módulos apropiados de la estructura del programa. Como técnica de diseño los pasos significativos son los dos primeros, mientras que el tercero corresponde más bien a la fase de codificación. Diseño Estructurado Esta técnica de diseño es el complemento del llamado análisis estructurado. Ambas técnicas coinciden en el empleo de los diagramas de flujo de datos (DFD), como medio fundamental de representación del modelo funcional del sistema. La tarea de diseño consiste en pasar de los DFD’s a los diagramas de estructura, la dificultad radica en que hay que establecer una jerarquía o estructura de control entre los diferentes módulos. Para establecer dicha jerarquía de control entre las diversas operaciones descritas en los DFD’s, la técnica de diseño estructurada recomienda hacer el análisis de flujo de datos global, es decir, realizar análisis denominados de flujo de transformación y de flujo de transacción. Análisis de flujo de Transformación: Consiste en identificar un flujo global de información desde los elementos de entrada al sistema hasta los de salida. Los procesos se dividen en tres entradas denominadas flujo de entrada, flujo de transformación y flujo de salida. Análisis del flujo de Transacción: Este se aplicará cuando el flujo de datos se pueda descomponer en varias líneas separadas, cada una de las cuales corresponde a una función o transacción distinta, de manera que solo una de estas líneas se activa para cada entrada de datos de tipo diferente, dicho análisis consiste en identificar el denominado centro de transacción a partir del cual se ramifican las lineas de flujo.
TECNICAS DE DISEÑO BASADO EN ABSTRACCIONES Estas técnicas de diseño surgen cuando se identifican con precisión los conceptos de abstracción de datos y de ocultación. La idea general es que los módulos se correspondan o bien con funciones o bien con tipos abstractos de datos. Las estructuras modulares resultantes pueden implementarse bien con lenguajes de programación que tengan facilidades para implementar abstracciones de datos tales como Modula-2, Ada o C. Por supuesto, pueden también implementarse con lenguajes de programación orientado a objetos, que poseen aún más facilidades (Java, PHP, C++).
Descomposición Modular basada en Abstracciones Como técnica de programación, consiste en ampliar el lenguaje existente con nuevas operaciones y tipos de datos, definidos por el , de forma que se simplifique la escritura de los niveles superiores del programa. Como técnica de diseño, consiste en dedicar módulos separados a la realización de cada tipo abstracto de datos y cada función importante. Esta técnica de diseño puede aplicarse tanto en forma ascendente como descendente. En forma descendente puede considerarse como una ampliación de la técnica de refinamiento progresivo. Si se aplica de forma ascendente se trata de ir ampliando las primitivas existentes en el lenguaje de programación y las librerías asociadas con nuevas operaciones y tipos de mayor nivel, más adecuados para el campo de la aplicación que se está diseñando. Método de Abbott En este método se sugiere una forma metódica de conseguir descripciones más formales que empleando notaciones precisas y no solo lenguaje natural. La idea es identificar en el texto de la descripción aquellas palabras o términos que puedan corresponder a elementos significativos del diseño como son los tipos de datos, los atributos y las operaciones. Los tipos de datos aparecen como sustantivos genéricos, los atributos como sustantivos y las operaciones como verbos. Algunos adjetivos pueden aparecer como valores de atributos.
TECNICAS DE DISEÑO ORIENTADAS A OBJETOS El diseño orientado a objetos es esencialmente igual al diseño basado en abstracciones, que de hecho se describe en muchos casos como “basado en objetos”. Los objetos sólo añaden algunas características adicionales, tales como la herencia y el polimorfismo. La idea global de las técnicas de diseño orientadas a objetos es que en la descomposición modular del sistema cada módulo contenga la descripción de una clase de objetos o de varias clases relacionadas entre sí. Diseño Orientado a Objetos La técnica general de diseño orientado a objetos se basa en los siguientes pasos: 1.
Estudiar y comprender el problema a resolver. Este paso debe haberse realizado ya en la fase de análisis de requisitos.
2.
Desarrollar en líneas generales una posible solución. Describirla suficientemente, al menos de una manera informal. Es posible que esto se haya hecho también durante la fase de análisis, aunque es más probable que se tenga que hacer o completar en la fase de diseño. Convendrá considerar varias alternativas y elegir la que se considere más apropiada.
3.
Formalizar dicha estrategia en términos de clases y objetos y sus relaciones. Esto puede hacerse mediante las siguientes etapas: a) Identificar los objetos (o clases), sus atributos y componentes. b) Identificar las operaciones sobre los objetos y asociarlas a la clase u objeto adecuado. c) Aplicar herencia donde sea conveniente. d) Describir cada operación en función de las otras, y subsanar posibles omisiones. e) Establecer la estructura modular del sistema, asignando clases y objetos a módulos.
Tema 3.1 Descomposición Modular Actividad por equipo: ¿Qué entendiste por módulo? ¿En qué consiste la descomposición modular? ¿Cuáles son los tres aspectos importantes que deben tomarse en cuenta para realizar una descomposición modular a un sistema? Menciona los tipos de Módulos más comunes en un sistema: Menciona las cualidades mínimas que debe contener una descomposición modular: Menciona las técnicas de Diseño que permiten descomposición modular: De acuerdo al sistema que desarrolla mencione los módulos en los que está divido el sistema: A su juicio estará completa la descomposición modular (conteste sí o no, en caso de contestar no mencione el o los módulos que faltan) ¿Cuál es el objetivo de descomponer un sistema en módulos?
Integrantes de equipo: Nota enviar esta información a
[email protected] Solo el Test