Documentos de Académico
Documentos de Profesional
Documentos de Cultura
Unidad 4: Modularización
Bienvenido a la unidad 4 de Programación Básica, al finalizar el estudio de esta unidad tendrás un
conocimiento y adquirirás habilidades de los siguientes temas:
● Utilización de librerías.
Introducción
En la primera unidad hemos visto que los algoritmos están presentes en todas nuestras acciones.
Aplicamos algoritmos para resolver situaciones cotidianas tales como cruzar una calle, preparar una
taza de té o leer un libro.
Si prestamos atención a los pasos que seguimos cuando, por ejemplo, preparamos una taza de té
naturalmente llegaremos a la siguiente enumeración:
1. Ir a la cocina
2. Calentar agua
3. Preparar la taza
4. Poner el agua en la taza
Sabemos también que “ir a la cocina” implica “caminar”, y “caminar” implica “dar un paso tras otro”
y “dar un paso” implica “levantar, adelantar y bajar un pie”, “levantar el pie” implica “tensionar un
conjunto de músculos”, etcétera.
Pensemos ahora este mismo algoritmo al revés, analizando desde la acción más particular hasta
llegar a la acción más general. Tendríamos que “tensionar los músculos” para “levantar un pie” para
“dar un paso” para “ir a la cocina”. Sería verdaderamente enloquecedor e impracticable.
1 ::
TSTI | UNIVERSIDAD TECNOLÓGICA NACIONAL . FACULTAD REGIONAL SANTA FE
TE +54 (342) 460 1579 / 2390 - FAX 469 0348 / tsti.frsf.utn.edu.ar
Conceptos iniciales
Metodología top-down
La metodología top-down propone pensar la solución de un problema en base a una secuencia de
acciones de nivel general cuyos pasos iremos refinando sucesivamente.
Esto significa partir de un análisis global y refinarlo hasta llegar a un análisis particular. Aplicando
esta metodología a la resolución de problemas computacionales podremos resolver situaciones
complejas con una simpleza y naturalidad verdaderamente sorprendente.
Módulos o subprogramas
Llamamos “módulo”, “rutina” o “subprograma” a cada una de las acciones que ejecutamos en
nuestro algoritmo y que luego vamos a desarrollar.
En el ejemplo de preparar una taza de té, invocamos a los módulos “caminar hacia la cocina”,
“calentar el agua”, etc. Luego tendremos que desarrollar cada uno de estos módulos para los cuales,
probablemente, necesitemos invocar a otros módulos que también tendremos que desarrollar.
Procurá familiarizarte con la vasta colección de clases y métodos que proporciona la API de Java
(https://docs.oracle.com/javase/7/docs/api/) y evita reinventar la rueda. Cuando sea posible,
reutiliza las clases y métodos de la API de Java. Esto reduce el tiempo de desarrollo de los programas
y evita que se introduzcan errores de programación.
2 ::
TSTI | UNIVERSIDAD TECNOLÓGICA NACIONAL . FACULTAD REGIONAL SANTA FE
TE +54 (342) 460 1579 / 2390 - FAX 469 0348 / tsti.frsf.utn.edu.ar
imaginar. Estas funciones son llamadas métodos. La mayoría de los métodos matemáticos operan
con doubles.
Los métodos matemáticos son llamados usando una sintaxis que es similar a la de los método print
que ya hemos visto:
El primer ejemplo asigna a raiz la raíz cuadrada de 17. El segundo ejemplo encuentra el seno de
1.5, el cual es el valor de la variable angulo. Java asume que los valores que usás con sin y las otras
funciones trigonométricas (cos, tan) están en radianes. Una forma de convertir de grados a radianes,
es dividir por 360 y multiplicar por 2. Convenientemente, Java provee a como valor preincorporado:
Notar que PI está todo en letras mayúsculas. Java no reconoce Pi, pi, ni pie.
Otro método útil en la clase Math es round (redondear), el cual redondea un valor de punto flotante
al entero más cercano y devuelve un int.
En este caso la multiplicación sucede primero, antes de que el método sea llamado. El resultado es
63 (redondeado hacia arriba desde 62.8319).
A continuación se sintetizan varios de los métodos de la clase Math y donde x y y son de tipo
double.
3 ::
TSTI | UNIVERSIDAD TECNOLÓGICA NACIONAL . FACULTAD REGIONAL SANTA FE
TE +54 (342) 460 1579 / 2390 - FAX 469 0348 / tsti.frsf.utn.edu.ar
Los métodos (también conocidos como funciones o procedimientos en otros lenguajes) permiten al
programador dividir un programa en módulos, por medio de la separación de sus tareas en unidades
autónomas. Los métodos que fuiste declarando en todos los programas se dice que son métodos
declarados por el programador. Las instrucciones en los cuerpos de los métodos se escriben sólo una
vez, y se reutilizan tal vez desde varias ubicaciones en un programa; además, están ocultas de otros
métodos.
Una razón para dividir un programa en módulos mediante los métodos es la metodología “divide y
vencerás”, que hace que el desarrollo de programas sea más fácil de administrar, ya que se pueden
construir programas a partir de piezas pequeñas y simples. Otra razón es la reutilización de software
(usar los métodos existentes como bloques de construcción para crear nuevos programas). A
menudo se pueden crear programas a partir de métodos estandarizados, en vez de tener que crear
código personalizado. Por ejemplo, en los programas anteriores no tuvimos que definir cómo leer los
valores de datos del teclado; Java proporciona estas herramientas en la clase Scanner. Una tercera
razón es para evitar la repetición de código. El proceso de dividir un programa en métodos
significativos hace que el programa sea más fácil de depurar y mantener.
4 ::
TSTI | UNIVERSIDAD TECNOLÓGICA NACIONAL . FACULTAD REGIONAL SANTA FE
TE +54 (342) 460 1579 / 2390 - FAX 469 0348 / tsti.frsf.utn.edu.ar
Para promover la reutilización de software, cada método debe limitarse de manera que realice una
sola tarea bien definida, y su nombre debe expresar esa tarea con efectividad. Estos métodos
hacen que los programas sean más fáciles de escribir, depurar, mantener y modificar.
Un método se invoca mediante una llamada, y cuando el método que se llamó completa su tarea,
devuelve un resultado, o simplemente el control al método que lo llamó. Una analogía a esta
estructura de programa es la forma jerárquica de la administración . Un jefe (el solicitante) pide a un
trabajador (el método llamado) que realice una tarea y que le reporte (devuelva) los resultados
después de completar la tarea. El método jefe, no sabe cómo el método trabajador, realiza sus
tareas designadas. Tal vez el trabajador llame a otros métodos trabajadores, sin que lo sepa el jefe.
Este “ocultamiento” de los detalles de implementación fomenta la buena ingeniería de software. La
figura siguiente muestra al método jefe comunicándose con varios métodos trabajadores en forma
jerárquica.
El método jefe divide las responsabilidades entre los diversos métodos trabajadores. Observá que
trabajador1 actúa como “método jefe” de trabajador4 y trabajador5.
Interfaz y encapsulamiento
Para comprender mejor los conceptos de interfaz y encapsulamiento, podemos pensar, por ejemplo,
en un teléfono celular. El teléfono tiene los botones numéricos a través de los que podemos marcar
un número y luego dos botones: uno verde para establecer la llamada y uno rojo para cortar la
comunicación.
Con esto, a cualquier usuario común le resulta muy fácil manejar un teléfono celular ya que, usando
adecuadamente este conjunto de botones, puede marcar un número y luego establecer la
comunicación deseada.
5 ::
TSTI | UNIVERSIDAD TECNOLÓGICA NACIONAL . FACULTAD REGIONAL SANTA FE
TE +54 (342) 460 1579 / 2390 - FAX 469 0348 / tsti.frsf.utn.edu.ar
Obviamente, nadie, salvo un estudiante de ingeniería en comunicaciones, se preocupará por
entender el proceso que se origina dentro del teléfono luego de que presionamos el botón verde
para establecer la llamada. Simplemente, nos abstraemos del tema ya que ese no es nuestro
problema.
Decimos entonces que el conjunto de botones numéricos más los botones rojo y verde constituyen
la interfaz del objeto teléfono celular. El usuario (nosotros) interactúa con el objeto a través de su
interfaz y no debe abrir la carcasa del teléfono para ver ni mucho menos para tocar su
implementación (cables, chips, transistores, etc.).
Justamente, en el teléfono celular, los botones están expuestos y los componentes electrónicos son
internos y están protegidos (encapsulados) dentro de la carcasa para que nadie los pueda tocar.
Funciones y subprogramas
Cuando aplicamos algoritmos a la resolución de problemas computacionales los módulos se
implementan como funciones.
Una función es un subprograma invocado desde el programa principal (o desde otra función) que
ejecuta una tarea determinada y luego, retorna el control al programa o función que la invocó.
La JVM carga la clase especificada por NombreClase y utiliza el nombre de esa clase para invocar al
método main. En el comando anterior, NombreClase es un argumento de línea de comandos para
la JVM, que le indica cuál clase debe ejecutar. Después del NombreClase, también puede especificar
una lista de objetos String (separados por espacios) como argumentos de línea de comandos, que
6 ::
TSTI | UNIVERSIDAD TECNOLÓGICA NACIONAL . FACULTAD REGIONAL SANTA FE
TE +54 (342) 460 1579 / 2390 - FAX 469 0348 / tsti.frsf.utn.edu.ar
la JVM pasará a su aplicación. Dichos argumentos pueden utilizarse para especificar opciones (por
ejemplo, un nombre de archivo) para ejecutar la aplicación.
Como veremos en la próxima unidad, la aplicación puede acceder a esos argumentos de línea de
comandos y utilizarlos para personalizar la aplicación.
En realidad, cualquier clase puede contener un método main. De hecho, cada uno de nuestros
ejemplos con dos clases podría haberse implementado como una sola clase.
Se podría colocar un método main en cada clase que declare. La JVM invoca sólo al método main en
la clase que se utiliza para ejecutar la aplicación. Algunos programadores aprovechan esto para crear
un pequeño programa de prueba en cada clase que declaran.
Composición
Tal como con las funciones matemáticas, los métodos en Java pueden ser compuestos, lo cual
significa que es posible usar una expresión como parte de otra. Por ejemplo, es posible usar
cualquier expresión como argumento de un método:
Esta sentencia toma el valor Math.PI, lo divide por dos y suma el resultado al valor de la variable
angulo. La suma es entonces pasada como un argumento al método cos. (Notar que PI es el
nombre de una variable, no un método, por lo que no hay argumentos, ni siquiera el argumento
vacío ()).
También podemos tomar el resultado de un método y pasarlo como argumento de otro:
En Java, la función log siempre usa base e, por lo que esta sentencia encuentra el logaritmo en base
e de 10 y después eleva e a esa potencia. El resultado es asignado a x; espero que sepas cuál es.
7 ::
TSTI | UNIVERSIDAD TECNOLÓGICA NACIONAL . FACULTAD REGIONAL SANTA FE
TE +54 (342) 460 1579 / 2390 - FAX 469 0348 / tsti.frsf.utn.edu.ar
Agregando nuevos métodos
Hasta ahora, sólo hemos estado usando los métodos que vienen preincorporados en Java, pero
también es posible agregar nuevos métodos. En realidad, ya hemos visto una definición de un
método: main1. El método llamado main es especial porque indica dónde empieza la ejecución de
un programa, pero la sintaxis de main es la misma que para cualquier definición de métodos:
Podés inventar cualquier nombre que quieras para tu método, excepto que no podés llamarlo main
o alguna otra palabra reservada de Java. La lista de parámetros especifica qué información hay que
proveer, si es que la hay, para poder usar (o llamar) la nueva función.
El único parámetro de main es String[] args, que indica que quienquiera que invoque a main
tiene que proveer un arreglo de Strings (nos detendremos en los arreglos en la unidad siguiente). Los
primeros dos métodos que vamos a escribir no tienen parámetros, por lo que la sintaxis se ve así:
Este método se llama nuevaLinea, y los paréntesis vacíos indican que no toma parámetros.
Contiene solamente una única sentencia, que imprime una cadena vacía, indicada por "". Imprimir
un valor de tipo String sin ninguna letra puede no parecer útil, pero hay que recordar que
println saltea a la siguiente línea después de imprimir, por lo que esta sentencia tiene el efecto de
saltar a la línea siguiente.
En main podemos llamar a este nuevo método usando una sintaxis similar a la forma en que
llamamos a los comandos pre incorporados de Java:
1
“principal” en inglés.
8 ::
TSTI | UNIVERSIDAD TECNOLÓGICA NACIONAL . FACULTAD REGIONAL SANTA FE
TE +54 (342) 460 1579 / 2390 - FAX 469 0348 / tsti.frsf.utn.edu.ar
System.out.println ("Segunda linea.");
}
Primera linea.
Segunda linea.
Notar el espacio extra entre las dos líneas. ¿Qué tal si quisiéramos más espacio entre las líneas?
Podríamos llamar al mismo método repetidamente:
O podríamos escribir un nuevo método, llamado tresLineas, que imprima tres nuevas líneas:
● Se puede hacer que un método invoque a otro método. En este caso, main llama a
tresLineas y tresLineas llama a nuevaLinea. Otra vez, esto es común y muy útil.
9 ::
TSTI | UNIVERSIDAD TECNOLÓGICA NACIONAL . FACULTAD REGIONAL SANTA FE
TE +54 (342) 460 1579 / 2390 - FAX 469 0348 / tsti.frsf.utn.edu.ar
● En tresLineas escribimos tres sentencias en la misma línea de código, lo cual es válido
sintácticamente (recordar que los espacios y las nuevas líneas usualmente no cambian el
significado de un programa).
● Por otro lado, es usualmente una mejor idea poner cada sentencia en una línea distinta, para
hacer tu programa más fácil de leer. A veces rompemos esa regla en este libro para ahorrar
espacio.
Hasta ahora, puede no estar claro por qué vale la pena crear todos estos nuevos métodos. En
realidad, hay un montón de razones, pero este ejemplo demuestra solamente dos:
1. Crear un nuevo método brinda una oportunidad de dar un nombre a un grupo de sentencias.
Los métodos pueden simplificar un programa al esconder un cómputo complejo detrás de un
comando simple, y usando una frase en castellano en lugar de código complicado. ¿Qué es
más claro, nuevaLinea o System.out.println ("")?
2. Crear un nuevo método puede hacer un programa más corto al eliminar código repetitivo.
Por ejemplo, ¿cómo harías para imprimir por pantalla nueve líneas nuevas consecutivas?
Podrías simplemente llamar tresLineas tres veces.
Clases y métodos
Reuniendo todos los fragmentos de código de la sección anterior, la definición completa de la clase
se ve así:
class NuevaLinea {
public static void nuevaLinea () {
System.out.println ("");
}
public static void tresLineas () {
nuevaLinea (); nuevaLinea (); nuevaLinea ();
}
public static void main (String[] args) {
System.out.println ("Primera linea.");
tresLineas ();
System.out.println ("Segunda linea.");
}
}
La otra clase que hemos visto es la clase Math. Ella contiene métodos llamados sqrt, sin, y muchos
otros. Cuando llamamos una función matemática, tenemos que especificar el nombre de la clase
(Math) y el nombre de la función. Es por eso que la sintaxis es ligeramente diferente entre los
métodos preincorporados y los métodos que escribimos nosotros:
La primera sentencia llama al método pow de la clase Math class (que eleva el primer argumento a
la potencia del segundo argumento)2. La segunda sentencia llama el método nuevaLinea, que Java
asume (correctamente) está en la clase NuevaLinea, que es la que estamos escribiendo. Si tratás de
llamar un método de una clase errónea, el compilador va a generar un error.
Por ejemplo, si tipearas: pow (2.0, 10.0);
El compilador va a decir algo como “No puedo encontrar un método llamado pow en la clase
NuevaLinea.” Si has visto este mensaje, podrías haberte preguntado por qué estaba buscando pow
en tu definición de clase. Ahora ya lo sabés.
2
pow viene de power, que significa potencia.
Por su parte, nuevaLinea llama al método pre incorporado println, que causa a su vez otra
desviación. Afortunadamente, Java es un experto en llevar la cuenta de dónde está, por lo que
cuando println termine, retoma por donde había dejado en nuevaLinea, luego retrocede hasta
tresLineas, y por último vuelve a main de manera que el programa pueda terminar.
¿Cuál es la moraleja de esta sórdida historia? Cuando leas un programa, no lo leas desde arriba hacia
abajo. En vez de eso, seguí el flujo de ejecución.
Parámetros y argumentos
Algunos de los métodos pre incorporados que hemos usado tienen parámetros, que son valores que
se le proveen para que puedan hacer su trabajo. Por ejemplo, si queremos encontrar el seno de un
número, tenemos que indicar qué número es. Por ello, sin toma un valor double como parámetro.
Para imprimir una cadena, hay que proveer la cadena, y es por eso que println toma un String
como parámetro.
Algunos métodos toman más de un parámetro, como pow, el cual toma dos doubles, la base y el
exponente.
Notar que en cada uno de esos casos tenemos que especificar no sólo cuántos parámetros hay, sino
también de qué tipo son. Por eso no debería sorprender que cuando escribimos una definición de
clase, la lista de parámetros indica el tipo de cada parámetro. Por ejemplo:
Este método toma un solo parámetro, llamado rigoberto, que tiene tipo String. Cualquiera sea ese
parámetro (y en este punto no tenemos idea de cuál es), es impreso dos veces. Yo elegí el nombre
Para llamar este método, tenemos que proveer un String. Por ejemplo, podríamos tener un
método main como este:
Alternativamente, si tuviéramos una variable de tipo String podríamos usarla como un argumento
en vez de lo anterior:
Notar algo muy importante aquí: el nombre de la variable que pasamos como argumento
(argumento) no tiene nada que ver con el nombre del parámetro (rigoberto). Permitime decirlo
nuevamente:
El nombre de la variable que pasamos como argumento no tiene nada que ver con el nombre del
parámetro.
Pueden ser el mismo o pueden ser diferentes, pero es importante darse cuenta de que no son la
misma cosa, excepto que sucede que tienen el mismo valor (en este caso la cadena "Nunca digas
nunca.").
El valor que proveas como argumento debe tener el mismo tipo que el parámetro del método que
invocás. Esta regla es muy importante, pero a menudo se complica en Java por dos razones:
● Si violás esta regla, el compilador suele generar un mensaje de error confuso. En vez de decir
algo como “Estás pasando un argumento de tipo erróneo a este método,” probablemente
diga algo del estilo de que no pudo encontrar un método con ese nombre que acepte un
argumento de ese tipo. Una vez que hayas visto este mensaje de error un par de veces, sin
embargo, vas a darte cuenta de cómo interpretarlo.
Para cada método hay un recuadro gris llamado frame que contiene los parámetros de los métodos
y las variables locales. El nombre del método aparece afuera del frame3. Como de costumbre, el
valor de cada variable es dibujado dentro de un recuadro con el nombre de la variable al lado de él.
3
frame significa marco.
Otra fuente común de confusión es que no hay que declarar los tipos de los argumentos al llamar un
método. Lo siguiente es erróneo!
En este caso, Java ya sabe el tipo de hora y minuto porque ha visto sus declaraciones. Es innecesario
e inválido incluir el tipo cuando se pasa como argumento. La sintaxis correcta es imprimirTiempo
(hora, minuto).
Ejercicio propuesto: Dibujar un diagrama de la pila que muestre el estado del programa cuando
main invoque a imprimirTiempo con los argumentos 11 y 59.
La respuesta a la tercera pregunta es “sí”, es posible escribir métodos que devuelvan valores”, y
vamos a hacerlo en un par de capítulos. Te dejo a vos que respondas las otras dos preguntas
intentando ver qué pasa. De hecho, siempre que tengas una pregunta acerca de qué es válido o
inválido en Java, una buena forma de averiguarlo es preguntarle al compilador.
Las reglas de promoción especifican qué conversiones son permitidas; esto es, qué conversiones
pueden realizarse sin perder datos. En el ejemplo anterior de sqrt, un int se convierte en double
sin modificar su valor. No obstante, la conversión de un double a un int trunca la parte fraccionaria
del valor double; por consecuencia, se pierde parte del valor. La conversión de tipos de enteros
largos a tipos de enteros pequeños (por ejemplo, de long a int) puede también producir valores
modificados.
Las reglas de promoción se aplican a las expresiones que contienen valores de dos o más tipos
simples, y a los valores de tipos simples que se pasan como argumentos para los métodos. Cada
valor se promueve al tipo “más alto” en la expresión. (En realidad, la expresión utiliza una copia
temporal de cada valor; los tipos de los valores originales permanecen sin cambios). En la siguiente
tabla se muestran los tipos primitivos y los tipos a los cuales se puede promover cada uno de ellos.
Observe que las promociones válidas para un tipo dado siempre se realizan a un tipo más alto en la
tabla. Por ejemplo, un int puede promoverse a los tipos más altos long, float y double.
Al convertir valores a tipos inferiores, se producirán distintos valores si el tipo inferior no puede
representar el valor del tipo superior (por ejemplo, el valor int 2000000 no puede representarse
como un short, y cualquier número de punto fl otante con dígitos después de su punto decimal no
pueden representarse en un tipo entero como long, int o short).
La llamada a este método convierte explícitamente el valor de valorDouble a un entero, para usarlo
en el método cuadrado. Por ende, si el valor de valorDouble es 4.5, el método recibe el valor 4 y
devuelve 16, no 20.25.
2. El alcance de la declaración de una variable local es a partir del punto en el cual aparece la
declaración, hasta el final de ese bloque.
3. El alcance de la declaración de una variable local que aparece en la sección de inicialización del
encabezado de una instrucción for es el cuerpo de la instrucción for y las demás expresiones en el
encabezado.
4. El alcance de un método o campo de una clase es todo el cuerpo de la clase. Esto permite a los
métodos no static de la clase utilizar cualquiera de los campos y otros métodos de la clase.
Resumen de la Unidad 4
La mayoría de los programas de cómputo que resuelven los problemas reales son mucho más
extensos que los programas que se presentan en las primeras unidades. La experiencia ha
demostrado que la mejor manera de desarrollar y mantener un programa extenso es construirlo a
partir de pequeñas piezas sencillas, o módulos. A esta técnica se le llama divide y vencerás.
Hasta aquí, hemos avanzado en las técnicas de diseño de algoritmos. Llega entonces el momento
de comenzar a diseñar estructuras de datos más complejas que permitan almacenar los datos y
accederlos de manera eficiente, en la unidad 5 trabajaremos en ello.
Glosario
punto flotante: Tipo de variable (o valor) que puede contener tanto fracciones como enteros. En
Java este tipo se llama double.
clase: Nombre para una colección de métodos. Hasta ahora, hemos usado las clases Math y System y
hemos escrito clases llamadas Hola y NuevaLinea.
método: Nombre para una secuencia de sentencias que realiza alguna función útil. Los métodos
pueden o no tomar parámetros, y pueden o no producir un resultado.
parámetro: Pieza de información que se provee al llamar un método. Los parámetros son como las
variables en el sentido de que contienen valores y son de algún tipo.
argumento: Valor que se provee cuando se llama a un método. Este valor debe tener el mismo tipo
que el parámetro correspondiente. En castellano, parámetro y argumento tienden a utilizarse
indistintamente.
llamar/invocar: Causar que un método sea ejecutado.