miércoles, 5 de noviembre de 2014

Programacion estructurada

Programación Estructurada

La visión clásica de la programación estructurada se refiere al control de ejecución. El control de
su ejecución es una de las cuestiones más importantes que hay que tener en cuenta al construir un
programa en un lenguaje de alto nivel. La regla general es que las instrucciones se ejecuten sucesivamente
una tras otra, pero diversas partes del programa se ejecutan o no dependiendo de que se cumpla alguna
condición. Además, hay instrucciones (los bucles) que deben ejecutarse varias veces, ya sea en número fijo o
hasta que se cumpla una condición determinada.
Sin embargo, algunos lenguajes de programación más antiguos (como Fortran) se apoyaban en una sola
instrucción para modificar la secuencia de ejecución de las instrucciones mediante una transferencia
incondicional de su control (con la instrucción goto, del inglés "go to", que significa "ir a"). Pero estas
transferencias arbitrarias del control de ejecución hacen los programas muy poco legibles y difíciles de
comprender. A finales de los años sesenta, surgió una nueva forma de programar que reduce a la mínima
expresión el uso de la instrucción goto y la sustituye por otras más comprensibles.
Esta forma de programar se basa en un famoso teorema, desarrollado por Edsger Dijkstra, que demuestra
que todo programa puede escribirse utilizando únicamente las tres estructuras básicas de control siguientes:

• Secuencia: el bloque secuencial de instrucciones, instrucciones ejecutadas sucesivamente, una detrás de
otra.
• Selección: la instrucción condicional con doble alternativa, de la forma "if condición then instrucción-1 else
instrucción-2".
• Iteración: el bucle condicional "while condición do instrucción", que ejecuta la instrucción repetidamente
mientras la condición se cumpla.
Los programas que utilizan sólo estas tres instrucciones de control básicas o sus variantes (como los bucles
for, repeat o la instrucción condicional switch-case), pero no la instrucción goto, se llaman estructurados.
Ésta es la noción clásica de lo que se entiende por programación estructurada (llamada también
programación sin goto) que hasta la aparición de la programación orientada a objetos se convirtió en la
forma de programar más extendida. Esta última se enfoca hacia la reducción de la cantidad de estructuras
de control para reemplazarlas por otros elementos que hacen uso del concepto de polimorfismo. Aun así los
programadores todavía utilizan las estructuras de control (if, while, for, etc.) para implementar sus
algoritmos porque en muchos casos es la forma más natural de hacerlo.
Una característica importante en un programa estructurado es que puede ser leído en secuencia, desde el
comienzo hasta el final sin perder la continuidad de la tarea que cumple el programa, lo contrario de lo que
ocurre con otros estilos de programación.
Este hecho es importante debido a que es mucho más fácil comprender completamente el trabajo que
realiza una función determinada si todas las instrucciones que influyen en su acción están físicamente
contiguas y encerradas por un bloque. La facilidad de lectura, de comienzo a fin, es una consecuencia de
utilizar solamente tres estructuras de control, y de eliminar la instrucción de transferencia de control goto.

La visión moderna de la programación estructurada: la segmentación
La realización de un programa sin seguir una técnica de programación produce frecuentemente un conjunto
enorme de sentencias cuya ejecución es compleja de seguir, y de entender, pudiendo hacer casi imposible la
depuración de errores y la introducción de mejoras. Se puede incluso llegar al caso de tener que abandonar
el código preexistente porque resulte más fácil empezar de nuevo.
Cuando en la actualidad se habla de programación estructurada, nos solemos referir a la división de un
programa en partes más manejables (usualmente denominadas segmentos o módulos). Una regla prácticapara lograr este propósito es establecer que cada segmento del programa no exceda, en longitud, de una
página de codificación, o sea, alrededor de 50 líneas.
Así, la visión moderna de un programa estructurado es un compuesto de segmentos, los cuales puedan estar
constituidos por unas pocas instrucciones o por una página o más de código. Cada segmento tiene
solamente una entrada y una salida, asumiendo que no poseen bucles infinitos y no tienen instrucciones que
jamás se ejecuten. Encontramos la relación entre ambas visiones en el hecho de que los segmentos se
combinan utilizando las tres estructuras básicas de control mencionadas anteriormente y, por tanto, el
resultado es también un programa estructurado.
Cada una de estas partes englobará funciones y datos íntimamente relacionados semántica o
funcionalmente. En una correcta partición del programa deberá resultar fácil e intuitivo comprender lo que
debe hacer cada módulo.
En una segmentación bien realizada, la comunicación entre segmentos se lleva a cabo de una manera
cuidadosamente controlada. Así, una correcta partición del problema producirá una nula o casi nula
dependencia entre los módulos, pudiéndose entonces trabajar con cada uno de estos módulos de forma
independiente. Este hecho garantizará que los cambios que se efectúen a una parte del programa, durante
la programación original o su mantenimiento, no afecten al resto del programa que no ha sufrido cambios.

Esta técnica de programación conlleva las siguientes ventajas:

a) El coste de resolver varios subproblemas de forma aislada es con frecuencia menor que el de abordar el
problema global.
b) Facilita el trabajo simultáneo en paralelo de distintos grupos de programadores.
c) Posibilita en mayor grado la reutilización del código (especialmente de alguno de los módulos) en futuras
aplicaciones.
Aunque no puede fijarse de antemano el número y tamaño de estos módulos, debe intentarse un
compromiso entre ambos factores. Si nos encontramos ante un módulo con un tamaño excesivo, podremos
dividir éste a su vez en partes (nuevos módulos) más manejables, produciéndose la sucesiva división siempre
desde problemas generales a problemas cada vez menos ambiciosos y, por tanto, de fácil desarrollo y
seguimiento. Así, esta división toma la forma de un árbol cuya raíz es el programa principal que implementa
la solución al problema que afrontamos utilizando uno o varios módulos que realizan partes de dicha
solución por sí solos o invocando a su vez otros módulos que solucionan subproblemas más específicos. A
esta aproximación se la denomina diseño descendente o top-down, como queda esquematizado en la
siguiente figura:
El carácter autocontenido de los módulos o librerías hace que pueda ocultarse el funcionamiento interno de
las funciones contenidas en un módulo, ya que para utilizarlas basta con saber con qué nombre y
argumentos se invocan y qué tipo de valores devuelven.Al reunirlas en un módulo, se realza la relación entre las mismas separándolas del resto del programa.
Esta ocultación de los detalles se denomina encapsulación y se alcanza dividiendo el código del programa en
dos ficheros diferenciados: un fichero (con extensión ".h") que incluye la declaración de los tipos de datos y
de las funciones gracias a lo cual se sabe cómo acceder y utilizar cada una de las mismas y otro (con
extensión ".c") que contiene el código de cada una de las funciones declaradas en el .h.
Al compilar este último queda transformado en código objeto (al cual ya no se puede acceder para su
lectura o modificación) y puede distribuirse conjuntamente con el fichero de declaración (el que acaba en
.h), para su uso por parte de terceros sin riesgo alguno de alteración de la funcionalidad original (ésta quedó
encapsulada u oculta).
Esto es así porque para hacer uso de las funciones incluidas en el módulo únicamente necesitaremos
conocer la información que nos proporciona el fichero de declaración: el nombre, tipos de datos de los
argumentos y valores de retorno de las funciones. No es necesario conocer los detalles de implementación
(sentencias que se ejecutan dentro de una función).
Ejemplo práctico:
Supongamos que queremos desarrollar una aplicación para que dos usuarios jueguen al ajedrez. En primer
lugar, necesitamos una serie de funciones asociadas al tablero: una que coloque todas las piezas de ambos
jugadores en las posiciones iniciales, otra que determine si se ha producido el final de la partida, otra que
confirme la presencia de una pieza del jugador en juego en una determinada casilla, otra que confirme que
el movimiento no se sale del tablero, o que ese tipo de pieza puede realizarlo, y una última que lleve a
efecto el movimiento de una pieza del jugador en juego (eliminando del tablero a otra pieza del jugador
contrario si procede). Para estas últimas puede ser preciso utilizar a su vez otro módulo que represente las
funciones de movimiento asociadas a cada tipo de pieza.
La descomposición en módulos del programa quedaría:
Así, el contenido del fichero de libreríatablero.h sería:
enum columnas { a, b, c, d, e, f, g, h };
struct tablero {
int posx;
enum columnas posy;
struct pieza mipieza;
struct tablero *siguiente;
}
int Final(struct tablero *mitablero);
void Inicio(struct tablero *mitablero);
int Pertenece(struct tablero *mitablero, int posx, enum columnas posy);
void Dibuja(struct tablero *mitablero);
int FueraTablero(struct tablero *mitablero, int x1, enum columnas y1, int x2,
enumcolumnas y2);int Valido_T(struct tablero *mitablero, int x1, enum columnas y1, int x2, enum
columnas y2);
void Mover(struct tablero *mitablero, int x1, enum columnas y1, int x2, enum
columnas y2);
Así, el fichero de declaración pieza.h incluiría el siguiente código:
enum color { blanco, negro };
enum tipo { peon, torre, caballo, alfil, reina, rey }
struct pieza {
enum color micolor;
enum tipo mitipo;
}
int Valido_P(struct pieza *mipieza, int x1, int y1, int x2, int y2);
De esta forma, con el conocimiento público de ambos ficheros de declaración (tablero.h, pieza.h) podremos
hacer uso de las funciones de los módulos tablero y pieza declaradas en estos ficheros, a pesar de nuestra
ignorancia completa de cómo fueron implementadas. Lo ignoramos, ya que no tenemos acceso a los
ficheros de implementación o código fuente (tablero.c, pieza.c); únicamente disponemos del código objeto
(ablero.o, pieza.o) para su ejecución posterior.

No hay comentarios:

Publicar un comentario