You are on page 1of 23

Programacin modular

A medida que avanza el tiempo, los problemas que se espera que un computador resuelva se vuelven ms y ms complejos. As, es en realidad muy extrao que se pueda disear un algoritmo simple que resuelva un problema real hoy en da, por lo que es necesario plantearse algn tipo de subdivisin que haga el problema abordable, en subproblemas manejables. En este tema se presentan las herramientas de programacin que permiten resolver problemas complejos mediante su descomposicin en otros ms simples.

Programacin estructurada
Es este paradigma de programacin el que se podra llamar original, procedimental, o tambin conocido como Divide y vencers. Se trata de dividir el problema a resolver en tareas a realizar, y estas tareas en una serie de procedimientos, para finalmente encontrar el algoritmo que mejor se encuadre en ellos. Fortran (creado en 1954, por Jhon programacin procedimental, aunque hoy en programacin la permiten. El lenguaje Pascal Niklaus Wirth), es muy conocido debido al programacin. Backus) es el lenguaje original de da la mayora de los lenguajes de (de principios de los 70, creado por refuerzo que haca de este tipo de

En este tipo de programacin, el programador debe concentrarse en que cada procedimiento tenga una interfaz consistente, describiendo los argumentos de entrada y los de salida, y que cada procedimiento sea independiente del resto.

PROCEDIMIENTOS Y FUNCIONES
Un algoritmo que resolviera un problema complejo, contendra cientos o miles de lneas de cdigo en su interior. Esto es inabarcable para cualquier programador, por lo que se utiliza el concepto de procedimientos y funciones para subdidivir el problema en partes. La idea es que cada una de estas partes contenga un conjunto de instrucciones que permita la ejecucin de algn proceso determinado y lgico desde el punto de vista humano. Dos ejemplos, funcin y procedimiento respectivamente:
FUNCION multiplicar(E a: ENTERO, E b: ENTERO): ENTERO RETORNA ( a * b ) FIN_FUNCION

PROCEDIMIENTO escribirEdad(E mensaje: CADENA, E edad: ENTERO) ESCRIBIR( mensaje, edad ) FIN_PROCEDIMIENTO

La descomposicin del software en tareas tambin se conoce con el nombre de top-down y fue presentada por primera vez por Niklaus Wirth. Este autor proporciona la siguiente visin de refinamiento: En cada paso (del refinamiento), una o varias instrucciones del programa dado, se descomponen en instrucciones ms detalladas. Esta descomposicin sucesiva o refinamiento de especificaciones termina cuando todas las instrucciones estn 1

expresadas en trminos de la computadora usada o del lenguaje de programacin Conforme se refinan las tareas, tambin los datos pueden ser refinados, descompuestos o estructurados siendo natural refinar las especificaciones del programa y los datos en paralelo Cada paso de refinamiento implica algunas decisiones de diseo. Es importante que el programador sea consciente de los criterios subyacentes (en las decisiones de diseo adoptadas) y de la existencia de soluciones alternativas Tpicamente, una descomposicin insuficiente de un problema en tareas conduce a la definicin de pocos procedimientos, cada uno de las cuales implementar mltiples funcionalidades. FUNCIONES Una funcin es una un conjunto de instrucciones, con un nombre asociado, que cumple las siguientes caractersticas: Tiene uno o ms parmetros de entrada. Tiene un parmetro exclusivamente de salida y de tipo simple (es decir, lo que devuelve). Muchos lenguajes de programacin no requieren el hecho de que el parmetro sea simple pero en esta asignatura se considerar la versin ms purista. Todos los valores de entrada son necesarios y suficientes para determinar el valor de salida. Su sintaxis es la siguiente:
FUNCION nombre_funcion(lista de parmetros formales): Tipo_de_salida CONSTANTES ... TIPOS ... VARIABLES ... INICIO instruccin 1 instruccin 2 .... RETORNA ( expresin ){ de Tipo_de_salida } FIN_FUNCION

A continuacin se muestra, como ejemplo, una funcin que devuelve la mayor de dos variables.

FUNCION max (E a: ENTERO, E b: ENTERO): ENTERO VARIABLES valorDeRetorno: ENTERO INICIO SI a > b valorDeRetorno a SINO valorDeRetorno b FINSI RETORNA ( valorDeRetorno ) FIN_FUNCION

PROCEDIMIENTOS Son conjuntos de instrucciones con un nombre asociado, al igual que las funciones, pero no devuelven ningn valor. Los parmetros pueden ser de entrada, salida o de entrada / salida. Su sintaxis es la siguiente:
PROCEDIMIENTO nombre_procedimiento(lista_de_parmetros_formales) CONSTANTES ... TIPOS ... VARIABLES ... INICIO instruccin 1 instruccin 2 .... FIN_PROCEDIMIENTO

Al no soportar el retorno, al contrario que las funciones, para devolver valores se usan parmetros de salida o de entrada/salida. PARMETROS DE UN PROCEDIMIENTO O FUNCIN Los parmetros o argumentos de una funcin o procedimiento son el mecanismo que permite el intercambio de datos entre la funcin o procedimiento que es llamado y el que realiza la llamada. En el momento de definir una funcin o procedimiento, es necesario especificar los parmetros, con el tipo asociado. En este momento, los parmetros se denominan formales. Por otra parte, en el momento de hacer la llamada a una funcin o procedimiento, cuando se utilizan literales o variables como lista de parmetros (que deben emparejarse con los parmetros formales en cuanto al tipo y orden). Estos parmetros se denominan reales.

ALGORITMO Mayor { a y b son parmetros formales de la funcin max } FUNCION max (E a: ENTERO, E b: ENTERO): ENTERO VARIABLES valorDeRetorno: ENTERO INICIO SI a > b valorDeRetorno a SINO valorDeRetorno b FINSI RETORNA ( valorDeRetorno ) FIN_FUNCION VARIABLES num1, num2, mayor: ENTERO INICIO LEER( num1 ) LEER( num2 ) mayor max( num1 , num2 ) { num1 y num2 son parmetros reales } ESCRIBIR( mayor ) FIM_ALGORITMO

Parmetros de entrada Permiten aportar datos a funcin o procedimiento por parte del algoritmo que le invoca. Un parmetro real asociado con un parmetro formal de entrada, puede ser un literal, una variable o incluso el valor que se obtendr como resultado de la evaluacin de una expresin. La definicin de un parmetro formal de entrada se hace anteponiendo el modificador E delante del nombre del parmetro formal. Modificando un parmetro formal de entrada nunca se modifica el correspondiente parmetro real. Parmetros de salida Permiten que la accin pueda devolver resultados de su ejecucin al algoritmo que invoca. El parmetro real asociado con un parmetro formal de este tipo debe ser necesariamente una variable en la que almacenar el resultado devuelto por la accin. Esa variable no tiene porque haber sido inicializada con anterioridad. Cualquier accin o modificacin realizada sobre el parmetro formal de salida se refleja, de forma inmediata, sobre el correspondiente parmetro real. Para la definicin de un parmetro formal de salida se debe anteponer el modificador S delante del nombre formal. Parmetros de entrada/salida Los parmetros de entrada/salida permiten aportar datos de entrada en un procedimiento o funcin, que sta los modifique y los devuelva como resultado al algoritmo que la invoca. El parmetro real asociado con un parmetro formal declarado de entrada /salida, necesariamente debe ser una variable donde almacenar el valor devuelto por el procedimiento o funcin invocada y debe haber sido inicializado previamente a la invocacin. La declaracin de un parmetro de entrada/salida se har anteponiendo el modificador E/S delante del nombre del parmetro formal en la definicin de la funcin. 4

DESCOMPOSICIN DE UNA TAREA EN PROCEDIMIENTOS Y FUNCIONES La mayor complejidad a la hora de aplicar programacin estructurada es saber decidir correctamente cundo extraer cdigo para colocarlo en un procedimiento o funcin, y cundo utilizar una u otra. El proceso de identificacin de procedimientos y funciones se basa en el empleo de cuestiones lgicas (experiencia y sentido comn), ciertas heursticas y una regla bsica: Existen procedimientos y funciones que realizan procesos de entrada de datos, otras de salida de datos y otras de procesamiento de datos. Un procedimiento o funcin no debe combinar los tres tipos de tareas. Entre las heursticas a tener en cuenta se encuentran las siguientes: 1. Cada vez que se necesita operar con ciertos tipos de datos compuestos (como registros o vectores) suele resultar necesario disponer de procedimientos para la lectura de esos datos, otras para la visualizacin de los mismos y finalmente otras que implementen las distintas operaciones necesarias. 2. Las operaciones de inicializacin de un algoritmo que se ejecutan antes de empezar a funcionar dicho algoritmo (lecturas de ficheros de disco, inicializacin de estructuras de datos) se colocan normalmente en un procedimiento (muchas veces llamado iniciar) si bien, de resultar ser este procedimiento demasiado extenso o complejo, se puede subdividir, a su vez, en otros procedimientos. Esto mismo ocurre con las operaciones que se deben ejecutar justo antes de terminar el algoritmo (volcados a disco, etc.). Por otra parte, si las operaciones son muy sencillas, no ser probablemente interesante realizar este proceso. 3. Las operaciones de bsqueda y ordenacin en cualquier estructura de datos se implementarn normalmente en procedimientos. 4. Cada una de las funcionalidades que un algoritmo proporcionar a un usuario es un procedimiento. Tomando el enunciado del problema, muchas veces se pueden identificar los procedimientos (de ms alto nivel) a crear prestando atencin a los verbos presentes. 5. Un indicativo de que un procedimiento o funcin es demasiado grande suele ser el nmero de lneas que ocupa: si contiene ms de 25 30 lneas (no se visualizar totalmente en una sola pantalla) es demasiado grande y, por lo tanto, debe ser descompuesta, a su vez, en procedimientos o funciones ms pequeos. 6. Cuando en un algoritmo se repita ms de una vez la misma tarea con una diferencia pequea, esta diferencia pequea se representar en los parmetros de un procedimiento o funcin que ser capaz de adaptarse a todas las circunstancias en las que pueda ser empleado. En todo caso, hablar de diferencia pequea implica que las instrucciones ejecutadas son las mismas y que, por ejemplo la nica diferencia se encuentra en el nmero de veces que se ejecuta una composicin iterativa o cuestiones muy similares que se puedan parametrizar. Un error muy comn es emplear un procedimiento para dos acciones totalmente diferentes empleando un parmetro que indique cul de ellas ejecutar. 7. La interfaz de un procedimiento o funcin se disear para asegurar la mxima reutilizacin. 5

CUESTIONES DE MBITO En este apartado se tratarn una serie de cuestiones adicionales a tener en cuenta cuando se habla de procedimientos y funciones, que engloban conceptos relacionados con el mbito de variables, los efectos colaterales, etc... MBITO DE UN PROCEDIMIENTO O FUNCIN En general, se entiende por mbito de un procedimiento o funcin M, el conjunto de otras funciones o procedimientos que pueden invocarla. MBITO DE VARIABLES Las variables slo se pueden referenciar desde la funcin o procedimiento en la que estn declaradas. As, es fundamental restringir el uso de variables a las funciones o procedimientos en las que estn declaradas para no sufrir errores de programacin debida a efectos laterales. El mbito de una variable local o parmetro formal v declarada dentro de una funcin o procedimiento M es la propia funcin o procedimiento, cuyas instrucciones pueden modificarla o consultarla. A veces, sto puede complicarse debido a que ciertos lenguajes de programacin, como Pascal, permiten crear funciones (locales) dentro de funciones, de manera que las variables locales y parmetros formales creados para la contenedora sirven para todas las locales. Otros lenguajes, como C++, permiten crear bloques dentro de funciones o procedimientos, de manera que las variables creadas dentro de ellos tienen como mbito slo el bloque mencionado, y sin embargo se puede acceder al resto de variables de la funcin. Por ejemplo:
int elevarA(int x, int y) { int toret = 1; { /* n slo tiene vida dentro de este bloque sin embargo, se puede acceder a x e y */ int n; for(n = 0; n < y; ++n) { toret = toret * x; } } return toret; }

Una funcin o procedimiento debe usar nicamente para su funcionamiento: Parmetros formales. Variables declaradas dentro de esa accin con nombre.

De esta manera, se evitarn efectos laterales con variables globales, como se discute a continuacin.

VARIABLES LOCALES Y GLOBALES Las variables que aparecen en un algoritmo o programa se dividen atendiendo a su mbito en locales y globales.

Variables locales: son aquellas cuyo mbito de actuacin se reduce a la funcin o procedimiento donde estn definidas. Fuera de su funcin o procedimiento, estas variables no sern conocidas. Variables globales: son aquellas definidas por el algoritmo o programa principal, y potencialmente, su mbito de actuacin se extiende a todas los procedimientos y funciones del algoritmo, siempre que no se hayan (re)definido en el interior de alguna de ellas, bien variables locales o bien como parmetros, con el mismo identificador.

El intercambio de informacin entre procedimientos y funciones debe producirse siempre a travs de los parmetros y nunca a travs de variables globales. Cualquier otro dato cuyo uso slo tenga sentido dentro de una funcin o procedimiento, se declarar como una variable local a sta, incluso en el caso de que una variable similar con el mismo identificador y tipo se use en otros procedimientos y funciones. Tal y como se ha comentado con respecto a las variables globales, una variable local de una funcin o procedimiento oculta a la variable global con el mismo identificador, tal y como se ve en el ejemplo ms abajo:
ALGORITMO ocultacin VARIABLES x, y: ENTERO FUNCION elevarA(x: ENTERO, y: ENTERO): ENTERO VARIABLES resultado: ENTERO INICIO resultado 1 DESDE i 0 HASTA y - 1 resultado resultado * x FIN_DESDE RETORNA resultado FIN_ALGORITMO INICIO LEER( x ) LEER( y ) ESCRIBIR( x^y = , elevarA( x, y ) ) FIN_ALGORITMO

En este ejemplo, la variable local 'x' oculta, dentro de la funcin elevarA(), a la variable x del algoritmo principal. Una cuestin adicional es que, en la mayora de lenguajes de programacin, incluyendo a C y C++, existe un punto de entrada al programa que es una funcin con un nombre determinado, en el caso de estos lenguajes, main(). Es posible elegir si las variables del algoritmo principal sern variables globales o locales de esta funcin. As, es posible implementar el anterior algoritmo sin que se produzca esta ocultacin.

/* ALGORITMO (sin) ocultacin */ int elevarA(int x, int y) { int toret = 1; int n; for(n = 0; n < y; ++n) { toret = toret * x; } return toret; } int main() int x; int y; int resultado; scanf( %d, &x ); scanf( %d, &y ); resultado = elevarA( x, y ); printf( %d\n, resultado ); }

De esta manera, es posible escribir un programa sin utilizar ni una sola variable global (que es lo ms aconsejable). Dado que las variables locales se almacenan en el interior de la propia funcin o procedimiento, slo deberemos declarar como globales aquellas variables, como estructuras o matrices, de gran tamao, que no caben en la pila( stack). Los detalles internos, que explican este problema, se detallan en la siguiente seccin sobre cuestiones de implementacin. EFECTOS LATERALES Efecto lateral es cualquier modificacin de una variable producida en un procedimiento o funcin en la que la variable no est declarada. Estos efectos deben evitarse pues introducen dependencias indeseables (la funcin o procedimiento depende entonces de una variable global, por ejemplo), que son causas de errores muy difciles de detectar. En el siguiente algoritmo, la salida es Falso, tal y como se pretenda, pero no se visualiza nada ms, cuando se pretenda avisar de que el nmero era negativo.

ALGORITMO efectosLaterales VARIABLES a: ENTERO PROCEDIMIENTO proc (E p: ENTERO) INICIO a abs( a ) { en este punto se produce un efecto lateral } SI ( p > a ) ESCRIBIR( Cierto ) SINO ESCRIBIR( Falso ) FIN_SI FIN_PROCEDIMIENTO INICIO a -3 proc( 2 ) SI ( a < 0 ) ESCRIBIR( Aunque 'a' era negativo ... ) { ... nunca se ejecuta } FIN_SI FIN_ALGORITMO

La correccin del efecto lateral se puede obtener con una simple modificacin del cdigo, que no modifique 'a'. Ahora, el resultado es el esperado.
ALGORITMO efectosLaterales VARIABLES a: ENTERO PROCEDIMIENTO proc (E p: ENTERO) INICIO SI ( p > abs( a ) ) { en este punto se produca un efecto lateral } ESCRIBIR( Cierto ) SINO ESCRIBIR( Falso ) FIN_SI FIN_PROCEDIMIENTO INICIO a -3 proc( 2 ) SI ( a < 0 ) ESCRIBIR( Aunque 'a' era negativo ... ) FIN_SI FIN_ALGORITMO

Sin embargo, a pesar de que con esta modificacin se corrige el efecto lateral, el algoritmo sigue sufriendo del mismo problema que lo caus: el procedimiento proc() que no slo utiliza sus parmetros y variables locales para trabajar, sino que confa en la existencia de una variable global a. Podemos corregir fcilmente este problema con un nuevo parmetro, e incluso dejar el algoritmo tal y como estaba en un principio.

ALGORITMO efectosLaterales VARIABLES a: ENTERO PROCEDIMIENTO proc (E p: ENTERO, E a: ENTERO) INICIO a abs( a ) SI ( p > a ) ESCRIBIR( Cierto ) SINO ESCRIBIR( Falso ) FIN_SI FIN_PROCEDIMIENTO INICIO a -3 proc( 2, a ) SI ( a < 0 ) ESCRIBIR( Aunque 'a' era negativo ... ) FIN_SI FIN_ALGORITMO

PRECEDENCIA DEL NOMBRE En un algoritmo pueden definirse identificadores de variables idnticos siempre y cuando se definen en procedimientos o funciones distintas. Cuando se ejecuta una instruccin y se puede recuperar el valor de varias variables con el mismo nombre, se presenta un conflicto de ambigedad que se resuelve mediante el criterio conocido como precedencia del nombre. Este criterio implica que se coge la variable definida en la funcin o procedimiento ms cercano usando el criterio de mbito de la variable en cuestin. As, tiene preferencia, como ya se ha visto, la variable con el mbito ms cercano o ms local. Como ejemplo, se muestra un pequeo algoritmo que utiliza un procedimiento con variables de entrada y de salida para ejemplificar lo visto hasta ahora. La salida del algoritmo se muestra a continuacin. A 1 1 B 1 25

10

ALGORITMO precedenciaDeNombre VARIABLES a, b: ENTERO PROCEDIMIENTO proc(E a: ENTERO; S b: ENTERO) INICIO b 25 a b + 5 FIN_PROCEDIMIENTO INICIO a 1 b 1 ESCRIBIR( a, b ) proc( 1, a ) ESCRIBIR( a, b ) FIN_ALGORITMO

CUESTIONES DE IMPLANTACIN
Se han descrito los comportamientos de los mecanismos de paso de parmetros, llamada a funciones o procedimientos. En esta seccin se describe qu sucede realmente, a nivel de sistema operativo, cuando estos mecanismos se activan. PROCESO DE INVOCACIN DE UN PROCEDIMIENTO O FUNCIN Al ejecutar un programa, el sistema operativo asigna cierta cantidad de memoria al mismo. El programa en ejecucin (proceso en memoria) organiza el espacio asignado atendiendo al siguiente esquema:
TEXT DATA STACK HEAP

TEXT es la zona de la memoria donde se almacenan las instrucciones del programa. Muchas veces, en TEXT se almacenan tambin las constantes, al ser esta zona de la memoria normalmente de slo lectura. En DATA se almacenan las variables globales (no las constantes). El STACK (o pila de llamadas) permite al sistema operativo seguir el flujo de ejecucin del programa. Finalmente, el HEAP es una zona ampliable de la memoria donde se almacenan estructuras de datos que pueden variar de tamao con el tiempo. Cuando aparece una llamada a una funcin o procedimiento M, se crea en la pila (stack) un registro de activacin o entorno de ejecucin, denominado marco o frame de la pila, en donde se alojan las variables locales y parmetros formales declarados en la funcin o procedimiento, as como, la direccin de retorno a la instruccin posterior a la invocacin. Una vez creado el entorno, se cede el control a la primera instruccin de M. Si M llama a otra funcin o procedimiento M', se crear un nuevo frame en la pila, y se 11

procede a la primera instruccin de M'. De este modo se pueden ir apilando llamadas (de ah, su nombre) mientras sea necesario. Finalmente, cuando la ejecucin del procedimiento o funcin M' involucrado se completa, se destruye su marco en la pila, se recupera el marco anterior, correspondiente a M (la que hizo la invocacin), y se establece el contador de programa a la direccin de retorno del marco eliminado. Mecanismos de paso de parmetros en los lenguajes de programacin Los mecanismos descritos aqu, de paso de parmetros de entrada, salida y entrada/salida, no suelen encontrarse en los lenguajes de programacin, por el contrario, lo ms tpico es encontrar mecanismos de paso por valor y paso por referencia. Paso de parmetros por valor Cuando se realiza la llamada de un procedimiento o funcin, se efecta una copia de la evaluacin de los parmetros reales, que se almacena en una variable local con el mismo nombre que el parmetro formal, dentro del registro de activacin. Esto implica que al finalizar la ejecucin, estos parmetros se destruyen, y los parmetros reales no se ven afectados. Es el mecanismo ms genrico de los lenguajes de programacin y en la mayora de los cuales no es necesario especificar ningn modificador del parmetro formal. El parmetro real puede ser cualquier expresin. Paso de parmetros por referencia En el paso de parmetros por referencia, en vez de trabajar con una copia de los parmetros reales almacenada en la pila de ejecucin, se trabaja directamente sobre el parmetro real, por lo que cualquier modificacin realizada sobre el parmetro formal es repercutida automticamente en el parmetro real. El compilador hace que los parmetros formales declarados en la funcin o procedimiento apunten a la misma direccin de memoria que los parmetros reales. Este mecanismo de paso de parmetros se usa para parmetros de salida y entrada/salida, por lo que el parmetro real debe ser una variable. Muchas veces este tipo de paso de parmetros, combinado con restricciones de solo lectura, se emplea en algunos lenguajes de programacin para sustituir al ineficiente paso de parmetros por valor en pasos de entrada. Ntese que si se pasara un vector por valor, se estara copiando todo su contenido.

Programacin modular
Una vez que los programas se fueron haciendo todava ms complejos y, por lo tanto, mayores, an fue necesario crear un nivel ms de divisin. As, ahora era necesario organizar todos los procedimientos y funciones de alguna forma. Los mdulos agrupan, precisamente, procedimentos y funciones afines, y tambin los datos que manipulan, de manera que aquellos que no son empleados por el resto de mdulos quedan ocultos la ellos. Modula-2 (desarrollado a finales de los 70, tambin por Niklaus Wirth, es el sucesor directo de Pascal) es un buen ejemplo de lenguaje de programacin basado en mdulos. Otro lenguaje que la soporta es Ada.

12

La meta de la programacin modular es obtener una organizacin de la arquitectura del software, con un diseo no orientado a la simple obtencin del programa final, sino tambin a su mantenimiento, a su reutilizacin y a poder probarlo y entenderlo de forma fcil. Para ello, los algoritmos complejos y los programas se estructuran en mdulos y stos se estructuran en funciones y procedimientos. Al fin y al cabo, recordemos que las funciones y procedimientos, son porciones de cdigo diseadas para realizar tareas muy especficas. Los mdulos deben agrupar funcionalidades del software a desarrollar, de tal forma que, en el desarrollo de una aplicacin de hoja de clculo, por poner un ejemplo, todo el manejo de operaciones matemticas resida en mismo mdulo. Tambin todas las funciones que tienen que ver con el manejo de frmulas definidas por el usuario, deben estar en otro mdulo aparte (que probablemente necesite el mdulo de matemticas para funcionar), y as sucesivamente. El objetivo final sera poder reutilizar estos mdulos en otras aplicaciones. La programacin modular persigue que cada mdulo y funcin o procedimiento, cumpla las siguientes caractersticas: Pequeo tamao: de esta forma el impacto de un cambio puede ser perfectamente aislado. Independencia modular: cuanto mayor es la independencia de los mdulos y los procedimientos y funciones, ms sencillo es trabajar con l. El diseo debe reducir la necesidad de compartir ficheros, datos, dispositivos y llamadas desde o hacia otros mdulos y procedimientos y funciones. Caractersticas de caja negra: visin exclusiva de la funcionalidad de un mdulo o procedimiento/funcin en base a sus entradas y salidas sin tener en cuenta como se realiza el proceso. Modelo conceptual: un diseo es ms fcil de mantener si el modelo de diseo empleado se ha basado en conceptos lgicos de organizacin, los cuales sean ms familiares y comprensibles para el personal de mantenimiento.
Trabajo de integracin

Trabajo de programacin de modulos

Aislamiento de los detalles (encapsulacin): en un sistema existen partes que reflejan la filosofa y otras que reflejan los detalles. Ambas partes deben disearse por separado para evitar que una variacin en los detalles afecte a la filosofa del sistema.

Para conseguir los objetivos marcados se necesitan estrategias de diseo. Una de las principales se basa en la disposicin de tareas, es decir, descomponer una tarea en otras ms sencillas que se hayan identificando en la tarea de partida.

13

Una vez descompuesto el software en tareas, normalmente cada una de ellas ser un procedimiento. Estos procedimientos se agruparn en mdulos distintos, conformando grupos que representen las distintas categoras basadas en el tipo de problema que resuelven conceptualmente. La descomposicin en tareas persigue la reduccin del tamao de los procedimientos hasta conseguir un sistema ms fcil de entender y modificar, minimizando la duplicidad del cdigo, as como la creacin de mdulos y procedimientos o funciones tiles y reutilizables. De hecho, si se desarrolla un mdulo con varios procedimientos y funciones para, por ejemplo, funciones matemticas, este podr ser reutilizado en aplicaciones futuras. Para medir la calidad de un diseo modular se emplean las medidas de cohesin y acoplamiento que se pueden aplicar sobre mdulos y procedimientos o funciones.

Cohesin: es la medida de la fuerza o relacin funcional de los elementos de un mdulo (entendiendo por elementos, los procedimientos o funciones que lo componen, las definiciones de los datos y las llamadas a otros mdulos). Un mdulo coherente se encarga de una tarea relativamente sencilla en un programa y requiere de poca interaccin con el resto de mdulos y/o procedimientos y funciones. Por ejemplo, en un mdulo de matemticas, las funciones contenidas deben estar todas relacionadas con clculos matemticos, exclusivamente. Acoplamiento: grado de interdependencia entre los distintos mdulos. En general, dos mdulos no deben compartir estructuras de datos o variables. Por otro lado, cada mdulo debe definir las funciones y procedimientos, estructuras de datos y variables que le resulten necesarias para la realizacin de su funcin especfica. As, el mdulo de matemticas ya comentado no debe necesitar en absoluto de otros mdulos para trabajar. Otros mdulos ms complejos pueden necesitar a su vez de mdulos que le sirvan para proveer de ms funcionalidad, pero siempre de la manera ms independiente posible.

Como ya se ha establecido, con respecto a las funciones y procedimientos, cada uno de ellos debe depender exclusivamente de los parmetros que intercambia con las dems procedimientos y funciones. Mejorar la calidad implica aumentar la cohesin y disminuir el acoplamiento, debido a las siguientes razones: Cuantas menos conexiones existan entre los mdulos, menos oportunidades habr de que el efecto de un mdulo sea contraproducente para otro. Si se desea tener la posibilidad de cambiar un mdulo por otro (con la misma funcionalidad) con el mnimo riesgo, es decir, que el cambio afecte lo menos posible a otros mdulos entonces el acoplamiento debe ser mnimo y la cohesin mxima. Mientras se est manteniendo un mdulo, es deseable que no sea necesario preocuparse en los detalles internos de otros mdulos.

MDULOS DE MAYOR NIVEL


Los lenguajes de programacin actuales permiten la creacin de mdulos y procedimientos o funciones. Modula-2 (sucesor de Pascal), fue el primero en soportar 14

los mdulos completamente usando unidades de compilacin independiente, conocidas como Module (estas encontraran su lugar en Turbo Pascal de Borland, como Units). Estos mdulos se componen de funciones o procedimientos que permiten la realizacin de tareas especficas. Se pueden entender los mdulos, que agrupan procedimientos y funciones con caractersticas comunes, como de mayor nivel con respecto a las funciones y procedimientos, que agrupan instrucciones que realizan algn clculo o cuya composicin resuelve un determinado problema. Una vez identificadas las tareas necesarias para resolver un problema, estas se agrupan en mdulos segn criterios como los datos con los que operan, si sirven para manejar un dispositivo especfico, o tienen una temtica comn, etc. Es decir, se agrupan por funcionalidad. En pseudocdigo los mdulos pueden representarse empleando la siguiente sintaxis:

15

MODULO nombreDelModulo INTERFAZ USA modulo1, modulo2,...modulon CONSTANTES constante1 : Tipo1 = valorc1 .... TIPOS Tipo1=def1 ... VARIABLES variable1: tipo1 .... FUNCION fn1(...) : ... PROCEDIMIENTO proc1(...) IMPLEMENTACIN USA modulo1, modulo2,...modulon; CONSTANTES constante1 : Tipo1 = valorc1 .... TIPOS tipo1=def1 ... VARIABLES variable1: tipo1 .... FUNCION fn1(...) : ... INICIO ... FIN_FUNCION PROCEDIMIENTO proc1(...) INICIO .... FIN_PROCEDIMIENTO PROCEDIMIENTO procAux(...) INICIO .... FIN_PROCEDIMIENTO INICIALIZACIN instruccin1 .... instruccinn FIN_MODULO { proc. Privado }

Estos mdulos de alto nivel pueden ser reutilizados totalmente y, por lo tanto, constituyen una buena forma para poder disear software. Los mdulos se soportan de manera distinta en cada lenguaje de programacin, como Ada o el ya mencionado Modula-2. En C y C++, es necesario separar cada mdulo en dos archivos: un archivo con extensin .h, que almacena la parte de interfaz (pblica), y otro con extensin .cpp, que almacena la parte de implementacin (privada). Se muestra un ejemplo a continuacin, sobre una librera de funciones estadsticas.

16

MODULO UtilesEstadsticos INTERFAZ CONSTANTES MaxVector : ENTERO = 100; TIPOS Vector = vector [MaxVector] de REAL {v: vector de entrada numElem: nmero de elementos en el vector} FUNCION calcularMedia(E v: Vector, E numElem: ENTERO): REAL FUNCION calcularDesvTipica(E v: Vector, E numElem: ENTERO): REAL PROCEDIMIENTO leerVector (S v: Vector, S numElem: ENTERO) PROCEDIMIENTO escribirVector(E v: Vector, E numElem: ENTERO) IMPLEMENTACIN FUNCION calcularMedia(E v: Vector, E numElem: ENTERO): REAL VARIABLES i: ENTERO suma: REAL INICIO suma 0 DESDE i 0 HASTA numElem 1 suma suma + v[ i ] FIN_DESDE RETORNA (suma / numElem) FIN_FUNCION FUNCION calcularDesvTipica(E v: Vector, E numElem: ENTERO): REAL VARIABLES i: ENTERO media: REAL sumaDiferencias: REAL INICIO sumaDiferencia 0 media calcularMedia( v, numElem ) DESDE i 0 HASTA numElem 1 sumaDiferencia abs( v[ i ] - media ) FIN_DESDE RETORNA (sumaDiferencia / numElem) FIN_FUNCION PROCEDIMIENTO leerVector (S v: Vector, S numElem: ENTERO) VARIABLES i: ENTERO INICIO { Leer el nmero mximo de elementos } REPETIR LEER( numElem ) HASTA ( numElem > 0 Y numElem < Max ) { Leer los elementos del vector } DESDE i 0 HASTA n - 1 HACER LEER ( v[ i ] ) FIN_DESDE FIN_PROCEDIMIENTO PROCEDIMIENTO escribirVector(E v: Vector, E numElem: ENTERO)

17

VARIABLES i: ENTERO INICIO DESDE i 0 HASTA numElem - 1 ESCRIBIR( v[ i ] ); FIN_DESDE FIN_PROCEDIMIENTO

Un algoritmo que hiciera uso del mdulo anterior sera el siguiente.


ALGORITMO MediaYdesviacion USA UtilesEstadsticos VARIABLES v: Vector numElem: ENTERO INICIO leerVector( v, numElem ); ESCRIBIR( La media es , calcularMedia( v, numElem ) ) ESCRIBIR( La desviacin tpica es ) ESCRIBIR( calcularDesvTipica( v, numElem ) ) FIN_DESDE FIN_ALGORITMO

Como se puede apreciar, se puede emplear toda la parte de interfaz del mdulo como si estuviera definido directamente en el algoritmo. Lo siguiente es la implementacin en C++ estructurado de lo anterior. Primeramente, el archivo estadutil.h, que se corresponde con la interfaz del mdulo.
// MODULO UtilesEstadisticos #ifndef ESTADUTIL_H #define ESTADUTIL_H const int MaxVector = 100; void leerVector(double v[MaxVector], int &numElem); void escribirVector(double v[MaxVector], int numElem); double calcularMedia(double v[MaxVector], int numElem); double calcularDesvTipica(double v[MaxVector], int numElem); #endif

A continuacin, el archivo estadutil.cpp, que se corresponde con la implementacin del mdulo.

18

#include "estadutil.h" #include <cstdio> #include <cmath> using namespace std; void leerVector(double v[MaxVector], int &numElem) { int i; // Pedir longitud vector do { printf( "Introduzca nm. elementos (entre 0 y %d):\n", MaxVector ); scanf( "%d", &numElem ); } while( numElem < 1 || numElem > MaxVector ); // Pedir los elementos del vector printf( "Introduzca %d valores, uno a uno.\n", numElem ); for(i = 0; i < numElem; ++i) { scanf( "%lf", &v[ i ] ); } } void escribirVector(double v[MaxVector], int numElem) { int i; printf( "[ " ); for(i = 0; i < numElem; ++i) { printf( "%.2f ", v[ i ] ); } printf( "]" ); } double calcularMedia(double v[MaxVector], int numElem) { int i; double toret = 0; for(i = 0; i < numElem; ++i) { toret += v[ i ]; } return ( toret / numElem ); } double calcularDesvTipica(double v[MaxVector], int numElem) { int i; double toret = 0; double media = calcularMedia( v, numElem ); for(i = 0; i < numElem; ++i) { toret += abs( v[ i ] - media ); } return ( toret / numElem );

19

Finalmente, se incluye la implementacin del pequeo algoritmo de prueba de la librera, medesv.cpp.


// ALGORITMO mediaYdesviacion #include "estadutil.h" #include <cstdio> int main() { double v[MaxVector]; int numElem; leerVector( v, numElem ); printf( "v = " ); escribirVector( v, numElem ); printf( "\n" ); printf( "La media es %.2lf, y la desv. tpica %.2lf\n", calcularMedia( v, numElem ), calcularDesvTipica( v, numElem ) ); }

Ejemplos
Se pretende construir una aplicacin de gestin de una coleccin de libros. Para cada libro se almacenar el ttulo, el autor y el ao de publicacin. Al arrancar la aplicacin los datos sobre la coleccin sern recuperados de un fichero (libros.dat) y cargados en memoria en una variable de tipo ColeccionLibros. Durante la ejecucin de la aplicacin se trabajar sobre esta variable para mejorar la eficiencia de la aplicacin y justo antes de salir de la utilidad se realizar un volcado de los datos al fichero. Los libros se dispondrn en una estructura de datos adecuada que debe mantenerse ordenada segn el campo ttulo (de Libro) tanto en el fichero como en memoria. Al insertar un libro en la coleccin, este, se insertar en una posicin libre. Disee un algoritmo que permita gestionar la coleccin de libros, de forma quesea posible aadir nuevos libros, buscar los datos de un libro por su ttulo y listar todos los libros ordenados por ttulo. Solucin: Lo primero que se debe hacer es identificar una estructura de datos adecuada. En primer lugar, es necesario representar un libro, a la vez que es necesario tener tambin una coleccin de libros. La estructura de datos para representar varios elementos del mismo tipo es un vector. As: Para representar un libro escogemos un registro en el que se almacenan distintos elementos de distintos tipos:

20

TIPOS Libro = REGISTRO titulo: CADENA autor: CADENA anho: ENTERO FIN_REGISTRO

Para representar un listado de libros (listado de varios elementos del mismo tipo) parece conveniente emplear una estructura de datos de tipo vector.

CONSTANTES MaxLibros = 500; TIPOS ColeccionLibros = VECTOR [MaxLibros] DE Libro;

Ahora bien, en el vector no siempre tienen que existir MaxLibros elementos y, por lo tanto, para representar un conjunto de libros que puede adoptar distintos tamaos, se debe acompaar al vector informacin que indique la cantidad de elementos vlidos en el vector. En este sentido, se debe colocar un nuevo tipo que indique el nmero de elementos vlidos de la coleccin. Esto obligara a tener en cuenta ambas variables (el vector y el nmero de elementos), cada vez que esta estructura de datos (que se pretende que trabaje conjuntamente) fuese necesaria en un procedimiento o funcin. Por ello, suele ser mucho ms til (y cmodo) reunir ambas variables en una sola estructura.
CONSTANTES MaxLibros = 500; TIPOS ListaLibros = VECTOR [MaxLibros] DE Libro; Libro = REGISTRO titulo: CADENA autor: CADENA anho: ENTERO FIN_REGISTRO ColeccionLibros = REGISTRO libros: ListaLibros numLibros: ENTERO FIN_REGISTRO

Cada vez que se emplee un dato de tipo coleccin siempre ser necesario el tamao. Una vez que se han tomado una decisin sobre los tipos de datos adecuados se procede a detectar los procedimientos y funciones que pueden surgir en el proyecto. CargarDatos y VolcarDatos como acciones que hay que realizar justo despus de arrancar la aplicacin y justo antes de salir de la misma. Una accin con nombre para cada uno de las funcionalidades del programa: Insertar, Buscar y Listar. La lectura y escritura de estructuras de datos de tipo Libro: LeerLibro y VisualizarLibro Las operaciones de bsqueda sobre vectores entre las que se incluyen la bsqueda de una posicin para insertar y la bsqueda de datos concretos. En este

21

caso, se trata de BusqOrdenada, un proceso optimizado de bsqueda del que se hablar en el tema 4. Una vez detectadas los procedimientos que son necesarios, se procede a detectar las dependencias que existen entre ellos. En este sentido se ha detectado lo siguiente Los procedimientos Insertar y Buscar necesitarn poder llamar al procedimiento BusqOrdenada para conocer la posicin donde insertar o donde buscar. Los procedimientos Listar y Buscar necesitarn poder llamar al procedimiento VisualizarLibro para visualizar todos los libros de la coleccin o el libro que se busca en una coleccin. El procedimiento Insertar necesita poder llamar a LeerLibro para poder leer los datos de un libro.

Una vez identificadas las dependencias se escoge una distribucin de procedimientos en el diseo que permita satisfacer las dependencias teniendo en cuenta las reglas del mbito de procedimientos y funciones. En la siguiente figura se representa esta distribucin de procedimientos y funciones:

ColeccionLibros CargarDatos VolcarDatos BusqOrdenada Insertar Buscar LeerLibro VisualizarLibro Listar

Una vez escogida la estructura bsica se procede a realizar una deteccin de los parmetros que necesita cada accin con nombre para poder operar y, en funcin de ellos, se determinar si los procedimientos y funciones son procedimientos o funciones.

22

ColeccionLibros
PROC

CargarDatos
E NomFich: Cadena S col: ColeccionLibros S n: ENTERO

VolcarDatos
E NomFich: Cadena E col: ColeccionLibros E n: ENTERO

BusqOrdenada
E col: ColeccionLibros E t: ENTERO E e: Cadena

Listar
E col: ColeccionLibros E n: ENTERO

PROC

Buscar
PROC

PROC

Insertar
E/S col: ColeccionLibros E/S n: ENTERO PROC PROC

E col: ColeccionLibros E n: ENTERO

VisualizarLibro
E libro:Libro PROC

LeerLibro
S libro: Libro PROC

Como hay pocos procedimientos (menos de 10), se ha optado por no emplear mdulos de nivel superior. En el caso de que el nmero de procedimientos fuese superior a 10 sera ms conveniente plantearse el realizar una divisin en unidades de compilacin independiente separadas. En cada unidad de compilacin independiente se colocan los procedimientos y funciones que hacen referencia a un tipo concreto de datos, como por ejemplo la ColeccionLibros.

23

You might also like