Diseño estructurado

En programación y diseño de algoritmos, el diseño estructurado persigue elaborar algoritmos que cumplan la propiedad de modularidad. Para ello, dado un problema que se pretende resolver mediante la elaboración de un programa de ordenador, se busca dividir dicho programa en módulos siguiendo los principios de diseño de descomposición por refinamientos sucesivos, creación de una jerarquía modular y elaboración de módulos independientes.

Etapas del diseño estructurado

Descomposición

Para ello se requiere un adecuado análisis de dicho problema, siendo necesario definir primeramente el problema, para lo cual deberá contener una detallada pero concisa descripción del mismo. Un problema bien definido es aquel que lleva implícitas tanto una situación inicial como final claras.

¿Por qué descomponer un problema en partes? Experimentalmente, está comprobado que:

  • Un problema complejo cuesta más de resolver que otro más sencillo (de Perogrullo).
  • La complejidad de un problema global es mayor que el valor de las complejidades de cada una de sus partes por separado.

Según esto, merece la pena el esfuerzo de dividir un problema grande en subproblemas más pequeños. Si el objetivo es elaborar un programa para resolver dicho problema grande, cada subproblema (menos complejo) podrá ser resuelto por un módulo (subalgoritmo) relativamente fácil de implementar (más que el programa global no dividido). Ahora la cuestión es ¿cómo realizar la descomposición?; realizando un estudio descendente Top-Down que nos lleve desde la concepción del problema (programa o algoritmo) global hasta identificar sus partes (módulos). Esta técnica se repite aplicando una estrategia llamada de refinamiento sucesivo propuesta por el experto en Ciencias de la Computación Niklaus Wirth, que consiste precisamente en volver a aplicar el estudio descendente Top-Down a cada subproblema una y otra vez hasta obtener subproblemas suficientemente pequeños que puedan ser resueltos por módulos que cumplan, en la medida de lo posible, las características deseables en un módulo en el ámbito de la programación. En palabras del propio Niklaus Wirth:

  • En cada paso (del refinamiento), una o varias instrucciones del programa dado se descomponen en instrucciones más detalladas. Esta descomposición sucesiva o refinamiento de especificaciones termina cuanto todas las instrucciones están expresadas en términos de la computadora usada o del lenguaje de programación...
  • Conforme se refinan las tareas, también los datos pueden ser refinados, descompuestos o estructurados, siendo lo natural refinar las especificaciones del programa y de los datos en paralelo.
  • Cada paso de refinamiento implica algunas decisiones de diseño. Es importante que el programador sea consciente de los criterios subyacentes (en las decisiones de diseño adoptadas) y de la existencia de soluciones alternativas...

Problema del refinamiento sucesivo

¿Cuándo parar el refinamiento? Un refinamiento excesivo podría dar lugar a un número tan grande de módulos que haría poco práctica la descomposición. Se tendrán en cuenta estos criterios para dejar de descomponer:

  • Cuando no haya tareas bien definidas.
  • Cuando la interfaz de un módulo sea tan complicada como el propio módulo.

Jerarquía de módulos

Ésta es una consecuencia directa de la descomposición del problema mediante refinamientos sucesivos. El resultado será un conjunto de módulos estratificados en capas a modo de pirámide, donde en la cima habrá un único módulo que representará al programa global y en los niveles inferiores aparecerán los módulos resultantes de las sucesivas divisiones.

Al final, debe obtenerse una estructura piramidal donde los módulos de los niveles superiores se encargan de las tareas de coordinación, lógica de la aplicación y manipulación de los módulos inferiores; estos otros deberán realizar tareas de cálculo, tratamiento y entrada/salida de información.

Independencia

Ver Independencia en Características de un módulo.

Evaluando el diseño

Para evaluar o determinar cómo es de bueno un diseño estructurado se utilizan los conceptos de acoplamiento y cohesión; éstos están muy relacionados entre sí, tanto que difícilmente se puede variar uno sin que eso afecte al otro. También cabe decir que estos conceptos no son medidas que se puedan cuantificar numéricamente; son más bien magnitudes cualitativas. También se tienen en consideración los conceptos de fan-in y fan-out.

Acoplamiento

Se define como el grado de interdependencia que hay entre los distintos módulos de un programa; lo deseable es que esta interdependencia sea lo menor posible, es decir, un bajo acoplamiento. Los niveles de acoplamiento, ordenados de menor (más deseable) a mayor (menos deseable), son:

  • Acoplamiento normal.- Un módulo llama a otro de un nivel inferior y tan solo intercambian datos (parámetros de entrada/salida). Dentro de este tipo de acoplamiento podemos encontrarnos tres subtipos, dependiendo de los datos que intercambien los módulos:
    • Acoplamiento de datos: Los módulos se comunican mediante parámetros.
    • Acoplamiento de marca o por estampado: Los módulos se pasan datos con estructura de registro. No es muy deseable si el módulo receptor sólo requiere parte de los datos que se le pasan.
    • Acoplamiento de control: Los datos que se intercambian entre los módulos son controles. Debido a que en este subtipo un módulo controla la ejecución del otro, no es un buen acoplamiento, ya que impide que sean totalmente independientes.
  • Acoplamiento Común.- Dos módulos acceden a un mismo recurso común, típicamente memoria compartida, una variable global o un fichero. Una variante de este tipo de acoplamiento es el acoplamiento externo:
    • Acoplamiento externo.- Los módulos están ligados a componentes externos. Por ejemplo, dispositivos de E/S, protocolos de comunicaciones, etc.
  • Acoplamiento de contenido.- Ocurre cuando un módulo necesita acceder a una parte de otro módulo.

Cohesión

Se define como la medida de fuerza o relación funcional existente entre las sentencias o grupos de sentencias de un mismo módulo. Un módulo cohesionado ejecutará una única tarea sencilla interactuando muy poco o nada con el resto de módulos del programa. Se persigue que los módulos tengan una alta cohesión.

En el diseño estructurado podemos encontrarnos con los siguientes siete tipos de cohesión (de la mejor o más deseable a la menos recomendable):

  • Cohesión funcional: Los elementos del módulo están relacionados en el desarrollo de una única función.
  • Cohesión secuencial: Un módulo realiza distintas tareas en secuencia, de forma que las entradas de cada tarea son las salidas de la tarea anterior. No es una mala cohesión si las tareas implicadas no son muy complejas y requieren pocas líneas de código.
  • Cohesión comunicacional: El módulo realiza actividades paralelas usando los mismos datos de entrada y salida. Como en el caso anterior, tampoco se trata de un mal tipo de cohesión si las tareas son relativamente sencillas.
  • Cohesión procedimental: El módulo tiene una serie de funciones relacionadas por un procedimiento efectuado por el código (a modo de biblioteca). Es similar a la secuencial, pero puede incluir el paso de controles. Será deseable que las funciones estén relacionadas o realicen tareas dentro del mismo ámbito (p.e. la biblioteca string.h de C contiene funciones para operar con cadenas de caracteres).
  • Cohesión temporal: Los elementos del módulo están implicados en actividades relacionadas con el tiempo.
  • Cohesión lógica: Las actividades que realiza el módulo tienen la misma categoría. Esto es: es como si se tuvieran partes independientes dentro del mismo módulo.
  • Cohesión casual o coincidente: Los elementos del módulo contribuyen a las actividades relacionándose mutuamente de una manera poco significativa. Este tipo de cohesión viola el principio de independencia y de caja negra de los módulos.

Fan-In y Fan-Out

Además de los dos conceptos anteriores, se deben tener en cuenta el grado de absorción (fan-in) y la diseminación del control (fan-out) de los módulos para garantizar la calidad del diseño.

  • Fan-In: También llamado grado de absorción, es el número de superordinados inmediatos que tiene el módulo en cuestión. Es conveniente maximizar el fan-in durante el proceso de diseño, ya que cada instancia de fan-in múltiple indica que se ha evitado la duplicación de código.
  • Fan-Out: También llamado diseminación del control, es el número de subordinados inmediatos que tiene el módulo en cuestión. Conviene no tener un fan-out ni muy alto ni muy bajo, ya que eso es un posible indicador de un diseño pobre. Si no es posible evitarlo, es preferible un fan-out bajo antes que uno alto.

Véase también

Este artículo ha sido escrito por Wikipedia. El texto está disponible bajo la licencia Creative Commons - Atribución - CompartirIgual. Pueden aplicarse cláusulas adicionales a los archivos multimedia.