6. Elementos de un Programa

En esta parte del capítulo presentamos los distintos elementos que forman parte de un programa. No pretende ser una exposición exhaustiva, pero sí es nuestro objetivo dar una visión global de los distintos aspectos que intervienen en un programa.

En algunos casos la presentación de los conceptos es muy superficial. Ya nos tomaremos el tiempo en los niveles posteriores de profundizar poco a poco en cada uno de ellos. Por ahora lo único importante es poderlos usar en casos limitados. Esta manera de presentar los temas nos va a permitir generar las habilidades de uso de manera incremental, sin necesidad de estudiar toda la teoría ligada a un concepto antes de poder usarlo.

6.1. Algoritmos e Instrucciones

Los algoritmos son uno de los elementos esenciales de un programa. Un algoritmo se puede ver como la solución de un problema muy preciso y pequeño, en el cual se define la secuencia de instrucciones que se debe seguir para resolverlo. Imagine, entonces, un programa como un conjunto de algoritmos, cada uno responsable de una parte de la solución del problema global.

Un algoritmo, en general, es una secuencia ordenada de pasos para realizar una actividad. Suponga, por ejemplo, que le vamos a explicar a alguien lo que debe hacer para viajar en el metro parisino. El siguiente es un algoritmo de lo que esta persona debe hacer para llegar a una dirección dada:

  1. Compre un tiquete de viaje en los puntos de venta que se encuentran a la entrada de cada una de las estaciones del metro.
  2. Identifique en el mapa del metro la estación donde está y el punto adonde necesita ir.
  3. Localice el nombre de la estación de metro más cercana al lugar de destino.
  4. Verifique si, a partir de donde está, hay alguna línea que pase por la estación destino.
  5. Si encontró la línea, busque el nombre de la misma en la dirección de destino.
  6. Suba al metro en el andén de la línea identificada en el paso anterior y bájese en la estación de destino.

Tarea 6

Objetivo: Reflexionar sobre el nivel de precisión que debe ser usado en un algoritmo para evitar ambigüedades.

Suponga que usted es la persona que va a utilizar el algoritmo anterior, para moverse en el metro de París. Identifique qué problemas podría tener con las instrucciones anteriores. Piense por ejemplo si están completas.

¿Se prestan para que se interpreten de maneras distintas? ¿Estamos suponiendo que quién lo lee usa su "sentido común", o cualquier persona que lo use va a resolver siempre el problema de la misma manera?

Utilice este espacio para anotar sus conclusiones:

Tarea 7

Objetivo: Entender la complejidad que tiene la tarea de escribir un algoritmo.

Esta tarea es para ser desarrollada en parejas:

  1. En el primer cuadrante haga un dibujo simple.
  2. En el segundo cuadrante escriba las instrucciones para explicarle a la otra persona cómo hacer el dibujo.
  3. Lea las instrucciones a la otra persona, quien debe intentar seguirlas sin ninguna ayuda adicional.
  4. Compare el dibujo inicial y el dibujo resultante.
Dibujo: Algoritmo
Haga una síntesis de los resultados obtenidos:

Cuando es el computador el que sigue un algoritmo (en el caso del computador se habla de ejecutar), es evidente que las instrucciones que le demos no pueden ser como las definidas en el algoritmo del metro de París. Dado que el computador no tiene nada parecido al "sentido común", las instrucciones que le definamos deben estar escritas en un lenguaje que no dé espacio a ninguna ambigüedad (imaginemos al computador de una nave espacial diciendo "es que yo creí que eso era lo que ustedes querían que yo hiciera"). Por esta razón los algoritmos que constituyen la solución de un problema se deben traducir a un lenguaje increíblemente restringido y limitado (pero a su vez poderoso si vemos todo lo que con él podemos hacer), denominado un lenguaje de programación. Todo lenguaje de programación tiene su propio conjunto de reglas para decir las cosas, denominado la sintaxis del lenguaje.

Existen muchos lenguajes de programación en el mundo, cada uno con sus propias características y ventajas. Como dijimos anteriormente, en este libro utilizaremos el lenguaje de programación Java que es un lenguaje de propósito general (no fue escrito para resolver problemas en un dominio específico), muy utilizado hoy en día en el mundo entero, tanto a nivel científico como empresarial.

Un programa de computador está compuesto por un conjunto de algoritmos, escritos en un lenguaje de programación. Dichos algoritmos están estructurados de tal forma que, en conjunto, son capaces de resolver el problema.

6.2. Clases y Objetos

Las clases son los elementos que definen la estructura de un programa. Tal como vimos en la etapa de análisis, las clases representan entidades del mundo del problema (más adelante veremos que también pueden pertenecer a lo que denominaremos el mundo de la solución). Por ahora, y para que se pueda dar una idea de lo que es un programa completo, imagine que los algoritmos están dentro de las clases, y que son estas últimas las que establecen la manera en que los algoritmos colaboran para resolver el problema global (ver figura 1.9). Esta visión la iremos refinando a medida que avancemos en el libro, pero por ahora es suficiente para comenzar a trabajar.

Fig. 1.9 Visión intuitiva de la estructura de un programa

Hasta ahora es claro que en un programa hay una clase por cada entidad del mundo del problema. Pero, ¿qué pasa si hay varias "instancias" (es decir, varios ejemplares) de alguna de esas entidades? Piense por ejemplo que en vez de crear un programa para manejar un empleado, como en el primer caso de estudio, resolvemos hacer un programa para manejar todos los empleados de una empresa. Aunque todos los empleados tienen las mismas características (nombre, apellido, etc.), cada uno tiene valores distintos para ellas (cada uno va a tener un nombre y un apellido distinto). Es aquí donde aparece el concepto de objeto, la base de toda la programación orientada a objetos. Un objeto es una instancia de una clase (la cual define los atributos que debe tener) que tiene sus propios valores para cada uno de los atributos. El conjunto de valores de los atributos se denomina el estado del objeto. Para diferenciar las clases de los objetos, se puede decir que una clase define un tipo de elemento del mundo, mientras que un objeto representa un elemento individual.

Piense por ejemplo en el caso del triángulo. Cada uno de los puntos que definen las aristas de la figura geométrica son objetos distintos, todos pertenecientes a la clase Punto. En la figura 1.10 se ilustra la diferencia entre clase y objeto para el caso del triángulo. Fíjese que la clase Punto dice que todos los objetos de esa clase deben tener dos atributos (x, y), pero son sus instancias las que tienen los valores para esas dos características.

Fig. 1.10a Diferencia entre clases y objetos para el caso de estudio del triángulo
  • La clase Triangulo tiene tres asociaciones hacia la clase Punto (punto1, punto2 y punto3). Eso quiere decir que cada objeto de la clase Triangulo tendrá tres objetos asociados, cada uno de ellos perteneciente a la clase Punto.
  • Lo mismo sucede con las dos asociaciones hacia la clase Color: debe haber dos objetos de la clase Color por cada objeto de la clase Triangulo.
  • Cada triángulo será entonces representado por 6 objetos conectados entre sí: uno de la clase Triangulo, tres de la clase Punto y dos de la clase Color.
Fig. 1.10b Diferencia entre clases y objetos para el caso de estudio del triángulo
  • Cada uno de los objetos tiene asociado el nombre que se definió en el diagrama de clases.
  • El primer punto del triángulo está en las coordenadas (10, 10).
  • El segundo punto del triángulo está en las coordenadas (20, 20).
  • El tercer punto del triángulo está en las coordenadas (50, 70).
  • Las líneas del triángulo son del color definido por el código RGB de valor (5, 170, 47). ¿A qué color corresponde ese código?
  • Este es sólo un ejemplo de todos los triángulos que podrían definirse a partir del diagrama de clases.
  • En la parte superior de cada objeto aparece la clase a la cual pertenece.

Para representar los objetos vamos a utilizar la sintaxis propuesta en UML (diagrama de objetos), que consiste en cajas con bordes redondeados, en la cual hay un valor asociado con cada atributo. Podemos pensar en un diagrama de objetos como un ejemplo de los objetos que se pueden construir a partir de la definición de un diagrama de clases. En el ejemplo 7 se ilustra la manera de visualizar un conjunto de objetos para el caso del empleado.

Ejemplo 7

Objetivo: Ilustrar utilizando una extensión del caso de estudio 1 la diferencia entre los conceptos de clase y objeto.

La extensión consiste en suponer que el programa debe manejar todos los empleados de una empresa, en lugar de uno solo de ellos.

  • Cada objeto de la clase Empleado tendrá un valor para cada uno de sus atributos y un objeto para cada una de sus asociaciones.
  • Esta clase define los atributos de todos los empleados de la empresa.
  • De manera intuitiva, una clase puede verse como un molde a partir del cuál sus objetos son construidos.
  • Cada empleado será representado con tres objetos: uno de la clase Empleado y dos de la clase Fecha.

  • Este es el primer ejemplo de un empleado de la empresa. Se llama Juan Ruiz, nació el 6 de enero de 1971, comenzó a trabajar en la empresa el 1 de agosto de 1990 y su salario es de dos millones de pesos.
  • Durante la ejecución de un programa pueden aparecer tantos objetos como sean necesarios, para representar el mundo del problema. Si en la empresa hay 500 empleados, en la ejecución del programa habrá 1500 objetos representándolos (3 objetos por empleado).

  • Este grupo de objetos representa otro empleado de la empresa.
  • Note que cada empleado tiene sus propios valores para los atributos y que lo único que comparten los dos empleados es la clase a la cual pertenecen, la cual establece la lista de atributos que deben tener.

6.3. Java como Lenguaje de Programación

Existen muchos lenguajes de programación en el mundo. Los hay de distintos tipos, cada uno adaptado a resolver distintos tipos de problemas. Tenemos los lenguajes funcionales como LISP o CML, los lenguajes imperativos como C, PASCAL o BASIC, los lenguajes lógicos como PROLOG y los lenguajes orientados a objetos como Java, C# y SMALLTALK.

Java es un lenguaje creado por Sun Microsystems en 1995, muy utilizado en la actualidad en todo el mundo, sobre todo gracias a su independencia de la plataforma en la que se ejecuta. Java es un lenguaje de propósito general, con el cual se pueden desarrollar desde pequeños programas para resolver problemas simples hasta grandes aplicaciones industriales o de apoyo a la investigación.

En esta sección comenzamos a estudiar la manera de expresar en el lenguaje Java los elementos identificados hasta ahora. Comenzamos por las clases, que son los elementos fundamentales de todos los lenguajes orientados a objetos. Lo primero que debemos decir es que un programa en Java está formado por un conjunto de clases, cada una de ellas descrita siguiendo las reglas sintácticas exigidas por el lenguaje.

Cada clase se debe guardar en un archivo distinto, cuyo nombre debe ser igual al nombre de la clase, y cuya extensión debe ser .java. Por ejemplo, la clase Empleado debe estar en el archivo Empleado.java y la clase Fecha en la clase Fecha.java.

Un programa escrito en Java está formado por un conjunto de archivos, cada uno de los cuales contiene una clase. Para describir una clase en Java, se deben seguir de manera estricta las reglas sintácticas del lenguaje.

Ejemplo 8

Objetivo: Mostrar la sintaxis básica del lenguaje Java para declarar una clase.

Utilizamos el caso de estudio del empleado para introducir la sintaxis que se debe utilizar para declarar una clase.

Archivo: Empleado.java

Clase: Empleado

public class Empleado
{
    // Aquí va la declaración de la clase Empleado
}

Archivo: Fecha.java

Clase: Fecha

public class Fecha
{
    // Aquí va la declaración de la clase Fecha
}

En el lenguaje Java, todo lo que va entre dos corchetes ("{" y "}") se llama un bloque de instrucciones. En particular, entre los corchetes de la clase del ejemplo 8 va la declaración de la clase. Allí se deben hacer explícitos tanto los atributos como los algoritmos de la clase. También es posible agregar comentarios, que serán ignorados por el computador, pero que le sirven al programador para indicar algo que considera importante dentro del código. En Java, una de las maneras de introducir un comentario es con los caracteres //, tal como se muestra en el ejemplo 8.

El programa del simulador bancario, por ejemplo, consta de 16 clases distribuidas de la siguiente manera:

  • 4 clases para el modelo del mundo, almacenadas en los archivos SimuladorBancario.java, CuentaCorriente.java, CuentaAhorros.java y CDT.java.
  • 8 clases para la interfaz usuario, en 8 archivos .java.
  • 4 clases para las pruebas del programa, en 4 archivos .java.

Es aconsejable en este momento mirar en la sección 8 de este capítulo la localización de dichos archivos en la página web. Vale la pena también dar una mirada al contenido de los archivos que vamos mencionando en esta parte.

Puesto que un programa puede estar compuesto por miles de clases, Java tiene el concepto de paquete, a través del cual es posible estructurar las clases por grupos jerárquicos. Esto facilita su localización y manejo. En la figura 1.11 se muestra la estructura de paquetes del caso del simulador bancario.

Fig. 1.11 Ejemplo de la estructura de paquetes del caso de estudio del simulador bancario
  • Las dieciséis clases del programa se dividen en 3 paquetes: uno con las clases de la interfaz de usuario (aquellas que implementan la ventana y los botones), uno con el modelo del mundo y un último paquete con las pruebas.
  • El nombre completo de una clase es el nombre del paquete en el que se encuentra, seguido del nombre de la clase.

Toda clase en Java debe comenzar por la definición del paquete en el cual está situada la clase, como se muestra en el siguiente fragmento de programa del caso de estudio del empleado:

package uniandes.cupi2.empleado;

/**
 *    Esta clase representa un empleado
 */
public class Empleado
{

}
  • El nombre del paquete es una secuencia de identificadores separados por un punto.
  • uniandes.cupi2.empleado.Empleado es el nombre completo de la clase.
  • En el momento de desarrollar un programa se deben establecer los paquetes que se van a utilizar. En nuestro caso, el nombre del paquete está conformado por el nombre de la institución (uniandes), seguida por el nombre del proyecto (cupi2) y luego el nombre del ejercicio del cual forma parte la clase (empleado).
  • Cada empresa de desarrollo sigue sus propias convenciones para definir los nombres de los paquetes.

En todo lenguaje de programación existen las que se denominan palabras reservadas. Dichas palabras no las podemos utilizar para nombrar nuestras clases o atributos. Hasta el momento hemos visto las siguientes palabras reservadas: package, public y class.

Un elemento de una clase se declara public cuando queremos que sea visible desde otras clases.

En el ejemplo anterior se puede apreciar otra manera de incluir un comentario dentro de un programa: se utilizan los símbolos /** para comenzar y los símbolos */ para terminar. El comentario puede extenderse por varios renglones sin ningún problema, a diferencia de los comentarios que comienzan por los símbolos // que terminan cuando se acaba el renglón. Los comentarios que se introducen como aparece en el ejemplo sirven para describir los principales elementos de una clase y tienen un uso especial que se verá más adelante en el libro.

6.4. Tipos de Datos

Cada lenguaje de programación cuenta con un conjunto de tipos de datos a través de los cuales el programador puede representar los atributos de una clase. En este nivel nos vamos a concentrar en dos tipos simples de datos: los enteros (tipo int), que permiten modelar características cuyos valores posibles son los valores numéricos de tipo entero (por ejemplo, el día en la clase Fecha), y los reales (tipo double), que permiten representar valores numéricos de tipo real (por ejemplo, el interés de una cuenta de ahorros). También vamos a estudiar un tipo de datos para manejar las cadenas de caracteres (tipo String), que permite representar dentro de una clase una característica como el nombre de una persona o una dirección. En los siguientes niveles, iremos introduciendo nuevos tipos de datos a medida que los vayamos necesitando.

En Java, en el momento de declarar un atributo, es necesario declarar el tipo de datos al cual corresponde, utilizando la sintaxis que se ilustra en el ejemplo que se muestra a continuación:

package uniandes.cupi2.empleado;

/**
* Esta clase representa un empleado
*/
public class Empleado
{
    //-------------------------------
    // Atributos
    //-------------------------------
    private String nombre; 
    private String apellido; 
    private double salario;
    ...
}
  • Inicialmente se declaran los atributos nombre y apellido, de tipo String (cadenas de caracteres).
  • Los atributos se declaran como privados (private) para evitar su manipulación desde fuera de la clase.
  • El atributo salario se declara de tipo double, puesto que es un valor real.
  • Con las tres declaraciones que aparecen en el ejemplo, el computador entiende que cualquier objeto de la clase Empleado debe tener valores para esas tres características.
  • Sólo quedó pendiente por decidir el tipo del atributo genero, que no corresponde a ninguno de los tipos vistos; eso lo haremos más adelante.

Para modelar el atributo "genero", debemos utilizar alguno de los tipos de datos con los que cuenta el lenguaje. Lo mejor en este caso es utilizar un atributo de tipo entero y usar la convención de que si dicho atributo tiene el valor 1 se está representando un empleado con género masculino y, si es 2, un empleado con género femenino. Este proceso de asociar valores enteros y una convención para interpretarlos es algo que se hace cada vez que los valores posibles de un atributo no corresponden directamente con los de algún tipo de datos. Fíjese que una cosa es el valor que usamos (que es arbitrario) y otra la interpretación que hacemos de ese valor. Ese punto será profundizado en el nivel 2.

public class Empleado
{
    ...

    /** 
     * 1 = masculino, 2 = femenino
     */
    private int genero;

    ...

}
  • Al declarar un atributo para el cual se utilizó una convención especial para representar los valores posibles, es importante agregar un comentario en la declaración del mismo, explicando la interpretación que se debe dar a cada valor.
  • En el ejemplo, decidimos representar con un 1 el valor masculino, y con un 2 el valor femenino.

El tipo de un atributo determina el conjunto de valores que éste puede tomar dentro de los objetos de la clase, lo mismo que las operaciones que se van a poder hacer sobre dicha característica.

En el diagrama de clases de UML, por su parte, usamos una sintaxis similar para mostrar los atributos. En la figura 1.12 aparece la manera en que se incluyen los atributos y su tipo en el caso de estudio del empleado. Dependiendo de la herramienta que se utilice para definir el diagrama de clases, es posible que la sintaxis varíe levemente.

Fig. 1.12 Ejemplo de la declaración en UML de los atributos de la clase Empleado

Lo único que nos falta incluir en el código Java es la declaración de las asociaciones. Para esto, vamos a utilizar una sintaxis similar a la presentada anteriormente utilizando el nombre de la asociación como nombre del atributo y el nombre de la clase como su tipo, tal como se presenta en el siguiente fragmento de código:

package uniandes.cupi2.empleado;

public class Empleado
{
    //-------------------------------
    // Atributos
    //-------------------------------
    private String nombre; 
    private String apellido; 
    private double salario; 
    private int genero;

    private Fecha fechaNacimiento;
    private Fecha fechaIngreso;

}
  • Las asociaciones hacia la clase Fecha las declaramos como hicimos con el resto de atributos, usando el nombre de la asociación como nombre del atributo.
  • El tipo de la asociación es el nombre de la clase hacia la cual está dirigida la flecha en el diagrama de clases.
  • El orden de declaración de los atributos no es importante.

En la figura 1.13 aparece el diagrama de clases completo del caso de estudio del empleado.

Fig. 1.13 Representación de la clase Empleado en UML

Tarea 8

Objetivo: Crear habilidad en la definición de los tipos de datos para representar las características de una clase.

Escriba en Java y en UML las declaraciones de los atributos (y las asociaciones) para las cinco clases del caso de estudio del simulador bancario.

Declaracíon en Java Descripción de la clase en UML
Declaracíon en Java Descripción de la clase en UML
Declaracíon en Java Descripción de la clase en UML
Declaracíon en Java Descripción de la clase en UML

6.5. Métodos

Después de haber definido los atributos de las clases en Java, sigue el turno para lo que hemos llamado hasta ahora "los algoritmos" de la clase. Cada uno de esos algoritmos se denomina un método, y pretende resolver un problema puntual, dentro del contexto del problema global que se quiere resolver. También se puede ver un método como un servicio que la clase debe prestar a las demás clases del modelo (o a ella misma si es el caso), para que ellas puedan resolver sus respectivos problemas.

Un método está compuesto por cuatro elementos:

  • Un nombre (por ejemplo, cambiarSalario, para el caso de estudio del empleado, que serviría para modificar el salario del empleado).
  • Una lista de parámetros, que corresponde al conjunto de valores (cada uno con su tipo) necesarios para poder resolver el problema puntual (Si el problema es cambiar el salario del empleado, por ejemplo, es necesario que alguien externo al empleado dé el nuevo salario. Sin esa información es imposible escribir el método). Para definir los parámetros que debe tener un método, debemos preguntarnos ¿qué información, que no tenga ya el objeto, es indispensable para poder resolver el problema puntual?
  • Un tipo de respuesta, que indica el tipo de datos al que pertenece el resultado que va a retornar el método. Si no hay una respuesta, se indica el tipo void.
  • El cuerpo del método, que corresponde a la lista de instrucciones que representa el algoritmo que resuelve el problema puntual.

Típicamente, una clase tiene entre cinco y veinte métodos (aunque hay casos en los que tiene decenas de ellos), cada uno capaz de resolver un problema puntual de la clase a la cual pertenece. Dicho problema siempre está relacionado con la información que contiene la clase. Piense en una clase como la responsable de manejar la información que sus objetos tienen en sus atributos, y los métodos como el medio para hacerlo. En el cuerpo de un método se explica entonces la forma de utilizar los valores de los atributos para calcular alguna información o la forma de modificarlos si es el caso.

El encabezado del método (un método sin el cuerpo) se denomina su signatura.

Ejemplo 9

Objetivo: Mostrar la sintaxis que se usa en Java para declarar un método.

Usamos para esto el caso de estudio del empleado, con tres métodos sin cuerpo, suponiendo que cada uno debe resolver el problema que ahí mismo se describe. La declaración que aquí se muestra hace parte de la declaración de la clase (los métodos van después de la declaración de los atributos).
Se deja un cuarto método al final como tarea para el lector; en este caso, a partir de la descripción, debe determinar los parámetros, el retorno y la signatura del método.


public void cambiarSalario( double pNuevoSalario)
{
    // Aquí va el cuerpo del método
}

Nombre: cambiarSalario

Parámetros: pNuevoSalario de tipo real. Si no se entrega este valor como parámetro es imposible cambiar el salario del empleado. Note que al definir un parámetro se debe dar un nombre al valor que se espera y un tipo.

Retorno: ninguno (void) puesto que el objetivo del método no es calcular ningún valor, sino modificar el valor de un atributo del empleado.

Descripción: cambia el salario del empleado, asignándole el valor que se entrega como parámetro.


public double darSalario( )
{
    // Aquí va el cuerpo del método
}

Nombre: darSalario

Parámetros: ninguno, puesto que con la información que ya tienen los objetos de la clase Empleado es posible resolver el problema.

Retorno: el salario actual del empleado, de tipo real. En la signatura sólo se dice el tipo de datos que se va a retornar, pero no se dice cómo se retornará.

Descripción: retorna el salario actual del empleado.


public double calcularPrestaciones( )
{
    // Aquí va el cuerpo del método
}

Nombre: calcularPrestaciones

Parámetros: ninguno. Al igual que en el método anterior, no se necesita información externa al empleado para poder calcular sus prestaciones.

Retorno: las prestaciones anuales a las que tiene derecho el empleado. Las prestaciones, al igual que el salario, son un número real.

Descripción: retorna el valor de las prestaciones anuales a las que tiene derecho el empleado.


Nombre: aumentarSalario

Parámetros:

Retorno:

Descripción: aumenta el salario del empleado en un porcentaje que corresponde a la inflación anual del país.


¿Cuáles son los métodos que se deben tener en una clase? Esa es una pregunta que se contestará en niveles posteriores. Por ahora, supongamos que la clase tiene ya definidos los métodos que necesita para poder resolver la parte del problema que le corresponde y trabajemos en el cuerpo de ellos. En el diagrama de clases de UML, se utiliza la tercera zona de la caja de una clase para poner las signaturas de los métodos, tal como se ilustra en la figura 1.14.

Fig. 1.14 Sintaxis en UML para mostrar las signaturas de los métodos de una clase

Tarea 9

Objetivo: Escribir y entender en Java la signatura de algunos métodos del caso de estudio del simulador bancario.

Complete la siguiente información, ya sea escribiendo la signatura del método que se describe, o interpretando la signatura que se da. Todos los métodos de esta tarea son de la clase CuentaAhorros.

public void consignarValor( double pValor )
{

}
Nombre:
Parámetros:
Retorno:
Descripción:
Nombre: darSaldo
Parámetros: ninguno.
Retorno: valor de tipo real.
Descripción: retorna el saldo de la cuenta de ahorros.

Signatura del Método:

Nombre: retirarValor
Parámetros: valor de tipo entero, que indica el monto que se quiere retirar de la cuenta de ahorros.
Retorno: ninguno.
Descripción: retira de la cuenta de ahorros el valor que se entrega como parámetro.

Signatura del Método:

Nombre: darInteresMensual
Parámetros: ninguno.
Retorno: valor de tipo real.
Descripción: retorna el interés mensual que paga una cuenta de ahorros.

Signatura del Método:

Nombre: actualizarSaldoPorPasoMes
Parámetros: ninguno.
Retorno: ninguno.
Descripción: actualiza el saldo de la cuenta de ahorros simulando que acaba de transcurrir un mes y que se deben agregar los correspondientes intereses ganados.

Signatura del Método:

6.6. La Instrucción de Retorno

En el cuerpo de un método van las instrucciones que resuelven un problema puntual o prestan un servicio a otras clases. El computador obedece las instrucciones, una después de otra, hasta llegar al final del cuerpo del método. Hay instrucciones de diversos tipos, la más sencilla de las cuales es la instrucción de retorno (return). Con esta instrucción le decimos al método cuál es el resultado que debe dar como solución al problema. Por ejemplo, si el problema es dar el salario del empleado, la única instrucción que forma parte del cuerpo de dicho método indica que el valor se encuentra en el atributo "salario". En el siguiente fragmento de programa se ilustra el uso de la instrucción de retorno.

public class Empleado
{
    //--------------------------------
    // Atributos
    //--------------------------------
    private String nombre; 
    private String apellido; 
    private double salario; 
    private int genero;
    private Fecha fechaNacimiento;
    private Fecha fechaIngreso;

    //-------------------------------
    // Métodos
    //-------------------------------
    public double darSalario( )
    {
        return salario;
    }
}
  • Tal como se había presentado antes, la declaración de la clase comienza con la declaración de cada uno de sus atributos (incluidas las asociaciones). Note que no hay diferencia sintáctica entre declarar algo de tipo entero (genero) y una asociación hacia la clase Fecha (fechaIngreso).
  • Después de los atributos, viene la declaración de cada uno de los métodos de la clase. Cada método tiene una signatura y un cuerpo.
  • Los métodos que van a ser utilizados por otras clases se deben declarar como públicos.
  • En el cuerpo del método se deben incluir las instrucciones para resolver el problema puntual que se le plantea. El cuerpo de un método puede tener cualquier número de instrucciones.
  • En el cuerpo de un método únicamente se puede hacer referencia a los atributos del objeto para el cual se está resolviendo el problema y a los parámetros, que representan la información externa al objeto que se necesita para resolver el problema puntual.
  • En el caso del método cuyo problema puntual consiste en calcular el salario del empleado, la solución consiste en retornar el valor que se encuentra en el respectivo atributo. Fácil, ¿no?
  • Es buena idea utilizar comentarios para separar la "zona" de declaración de atributos y la "zona" de declaración de métodos. Esta separación en zonas va a facilitar su posterior localización.

Todo método que declare en su signatura que va a devolver un resultado (todos los métodos que no son de tipo void) debe tener en su cuerpo una instrucción de retorno.

Cuando alguien llama un método sobre un objeto, éste "busca" dicho método en la clase a la cual pertenece y ejecuta las instrucciones que allí aparecen, utilizando sus propios atributos. Por esa razón, en el cuerpo de los métodos se puede hacer referencia a los atributos del objeto sin riesgo de ambigüedad, puesto que siempre se trata de los atributos del objeto al cual se le invocó el método. En el ejemplo anterior, si alguien invoca el método darSalario( ) sobre un objeto de la clase Empleado, dicho objeto va a su clase para establecer lo que debe hacer y la clase le explica que debe retornar el valor de su propio atributo llamado salario.

6.7. La Instrucción de Asignación

Los métodos que no están hechos para calcular un valor, sino para modificar el estado del objeto, utilizan la instrucción de asignación (=) para definir el nuevo valor que debe tener el atributo. Si existiera, por ejemplo, un método para duplicar el salario de un empleado, el siguiente sería el cuerpo de dicho método:

public class Empleado
{
    ...

    public void duplicarSalario( )
    {
        salario = salario * 2;
    }
}

En la parte izquierda de la asignación va el atributo que va a ser modificado (más adelante se extenderá a otros elementos del lenguaje, pero por ahora puede suponer que sólo se hacen asignaciones sobre los atributos). En la parte derecha va una expresión que indica el nuevo valor que debe guardarse en el atributo. Pueden formar parte de una expresión los atributos (incluso el que va a ser modificado), los parámetros y los valores constantes (como el 2 en el ejemplo anterior). Los elementos que forman parte de una expresión se denominan operandos. Adicionalmente en la expresión están los operadores, que indican cómo calcular el valor de la expresión. Los operadores aritméticos son la suma (+), la resta (-), la multiplicación (*) y la división (/).

En el siguiente fragmento de código vemos algunos métodos de la clase Empleado, que dan una idea del uso de la asignación, el retorno de valores y las expresiones:

public class Empleado
{
    ...
    public void cambiarSalario( double pNuevoSalario )
    {
        salario  =  pNuevoSalario;
    }

    public double calcularSalarioAnual( )
    {
        return  salario  *  12;
    }
}
  • El primer método cambia el salario del empleado, asignándole el valor recibido como parámetro. Recuerde que siempre se asigna a la variable que aparece en la parte izquierda el valor que aparece en la parte derecha.
  • El segundo método calcula el total al año que recibe el empleado por concepto de salario.

6.8. La Instrucción de Llamada de un Método

En algunos casos, como parte de la solución del problema, es necesario llamar un método de un objeto con el cual existe una asociación. Suponga que un empleado necesita saber el año en el que él ingresó a la empresa. Esa información la tiene el objeto de la clase Fecha que está siendo referenciado por su atributo fechaIngreso. Puesto que la clase Empleado no tiene acceso directo a los atributos de la clase Fecha, debe llamar el método de dicha clase que presta ese servicio (o que sabe resolver ese problema puntual). La sintaxis para hacerlo y el proceso de llamada (o invocación) se ilustran a continuación:

Diagrama de objetos para ilustrar la llamada del método: la empleada María Gómez ingresó a la empresa a trabajar en el año 2005.
public class Empleado
{
    ...

    public void miProblema( )
    {
        int valor = fechaIngreso.darAnio( );
        ...
    }
}
  • Dentro de un método de la clase Empleado se necesita saber el año de ingreso a la empresa.
  • Invocamos el método darAnio( ) sobre el objeto de la clase Fecha que representa la fecha de ingreso. Ese método debe retornar 2005 si el diagrama de objetos es el mostrado en la figura anterior.
  • Para pedir un servicio a través de un método, debemos dar el nombre de la asociación, el nombre del método que queremos usar y un valor para cada uno de los parámetros que hay en su signatura (ninguno en este caso).
  • El resultado de la llamada del método lo guardamos en una variable llamada valor, de tipo entero. Un poco más adelante se explica el uso de las variables.
public class Fecha
{
   ...

   public int darAnio( )
   {
       return anio;
   }
}
  • El método darAnio( ) de la clase Fecha se contenta con retornar el valor que aparezca en el atributo "anio" del objeto sobre el cual se hace la invocación.

Con la referencia al objeto y el nombre del método, el computador localiza el objeto y llama el método pedido pasándole la información para los parámetros. Luego espera que se ejecuten todas las instrucciones del método y trae la respuesta en caso de que haya una.

De la misma manera que un objeto puede invocar un método de otro objeto con el cual tiene una asociación, también puede, dentro de uno de sus métodos, invocar otro método de su misma clase. ¿Para qué puede servir eso? Suponga que tiene un método cuyo problema se vería simplificado si utiliza la respuesta que calcula otro método. ¿Por qué no utilizarlo? Esta idea se ilustra en el siguiente fragmento de código:

public class Empleado
{
    ...

    public double calcularSalarioAnual( )
    {
        return  salario  *  12;
    }

    public double calcularImpuesto( )
    {
        double total = calcularSalarioAnual( );
        return total * 19.5 / 100;
    }
}
  • Suponga que queremos calcular el monto de los impuestos que debe pagar el empleado en un año. Los impuestos se calculan como el 19,5% del total de salarios recibidos en un año.
  • Si ya tenemos un método que calcula el valor total del salario anual, ¿por qué no lo utilizamos como parte de la solución? Eso nos va a permitir disminuir la complejidad del problema puntual del método, porque nos podemos concentrar en la parte que "nos falta" para resolverlo.
  • Para invocar un método sobre el mismo objeto, basta con utilizar su nombre sin necesidad de explicar sobre cuál objeto queremos hacer la llamada. Por defecto se hace sobre él mismo.
  • Note que utilizamos una variable (total) como parte del cuerpo del método. Una variable se utiliza para almacenar valores intermedios dentro del cuerpo de un método. Una variable debe tener un nombre y un tipo, y sólo puede utilizarse dentro del método dentro del cual fue declarada. En el siguiente capítulo volveremos a tratar el tema de las variables.

Ejemplo 10

Objetivo: Ilustrar la construcción de los métodos de una clase.

Para el caso de estudio del simulador bancario, en este ejemplo se muestra el código de algunos métodos, en donde se pueden apreciar los distintos tipos de instrucción que hemos visto hasta ahora.

package uniandes.cupi2.simuladorBancario.mundo;

public class SimuladorBancario
{
    //-------------------------------------
    // Atributos
    //-------------------------------------
    private String cedula;
    private String nombre;

    private CuentaCorriente corriente; 
    private CuentaAhorros ahorros; 
    private CDT inversion;
    private int mesActual;
    ...
}
  • Declaración de los atributos de la clase que representa la cuenta bancaria. Note de nuevo la manera en que se declaran las relaciones con otras clases (como atributos, cuyo nombre corresponde al nombre de la asociación).

public void consignarCuentaCorriente( double pMonto )
{
    corriente.consignarMonto( pMonto );
}
  • Para depositar en la cuenta corriente un valor que llega como parámetro, la cuenta bancaria pide dicho servicio al objeto que representa la cuenta corriente, usando la asociación que hay entre los dos y el método consignarMonto() de la clase CuentaCorriente.
public double calcularSaldoTotal( )
{
    return  corriente.darSaldo( ) + 
            ahorros.darSaldo( ) + 
            inversión.calcularValorPresente( pMesActual );
}
  • Para calcular y retornar el saldo total de la cuenta bancaria, el método pide a cada uno de los productos que la componen que calcule su valor actual. Luego, suma dichos valores y los retorna como el resultado. Fíjese que una expresión puede estar separada en varias líneas, mientras no aparezca el símbolo ";" de final de una instrucción.

  • Para calcular el valor presente del CDT se le debe pasar como parámetro el mes en el que va la simulación.

public void pasarAhorroACorriente( )
{
    double temp =  ahorros.calcularSaldo( );
    ahorros.retirar( temp );
    corriente.consignarValor( temp );
}
  • Este método pasa todo el dinero depositado en la cuenta de ahorros a la cuenta corriente. Fíjese que es indispensable utilizar una variable (temp) para almacenar el valor temporal que se debe mover. ¿Se podría hacer sin esa variable? Las variables se declaran dentro del método que la va a utilizar y se pueden usar dentro de las expresiones que van en el cuerpo del método.

Si hay necesidad de convertir un valor real en un valor entero, se puede usar el operador de conversión (int). Dicho operador se utiliza de la siguiente manera:
int respuesta = ( int )( 1000 / 33 );
En ese caso, el computador primero evalúa la expresión y luego elimina las cifras decimales.

Tarea 10

Objetivo: Escribir el cuerpo de algunos métodos simples.

Escriba el cuerpo de los métodos de la clase CuentaBancaria (caso de estudio 2) cuya signatura aparece a continuación. Utilice los nombres de los atributos que aparecen en la declaración de la clase. Suponga que existen los métodos que necesite en las clases CuentaCorriente, CuentaAhorros y CDT.

public void ahorrar( double pMonto)
{



}

Pasa de la cuenta corriente a la cuenta de ahorros el valor que se entrega como parámetro (suponiendo que hay suficientes fondos).

public void retirarAhorro( double pMonto)
{


}

Retira un valor dado de la cuenta de ahorros (suponiendo que hay suficientes fondos).

public double darSaldoCorriente( )
{


}

Retorna el saldo que hay en la cuenta corriente. No olvide que éste es un método de la clase CuentaBancaria.

public void retirarTodo( )
{


}

Retira todo el dinero que hay en la cuenta corriente y en la cuenta de ahorros.

public void duplicarAhorro( )
{


}

Duplica la cantidad de dinero que hay en la cuenta de ahorros.

public void avanzarMesSimulacion( )
{


}

Avanza un mes la simulación de la cuenta bancaria.

Dentro de un método:

  • Para hacer referencia a un atributo basta con utilizar su nombre (salario).
  • Para invocar un método sobre el mismo objeto, se debe dar únicamente el nombre del método y la lista de valores para los parámetros (cambiarSalario( 2000000 )).
  • Para invocar un método sobre un objeto con el cual se tiene una asociación, se debe dar el nombre de la asociación, seguido de un punto y luego la lista de valores para los parámetros (fechaIngreso.darDia( )).

6.9. Llamada de Métodos con Parámetros

Este tema se profundizará en los capítulos posteriores. Por ahora sólo queremos dar una idea global del proceso de llamada de un método con parámetros. Para eso vamos a contestar siete preguntas:

  • ¿Cuándo necesita parámetros un método? Un método necesita parámetros cuando la información que tiene el objeto en sus atributos no es suficiente para resolver el problema que le plantean.
  • ¿Cómo se declara un parámetro? En la signatura del método se define el tipo de dato del parámetro y se le asocia un nombre. Es conveniente que este nombre dé una idea clara del valor que se va a recibir por ese medio.
  • ¿Cómo se utiliza el valor del parámetro? Basta con utilizar el nombre del parámetro en el cuerpo del método, de la misma manera en que se utilizan los atributos.
  • ¿Se puede utilizar el parámetro por fuera del cuerpo del método? No. En ningún caso.
  • Aquel que hace la llamada del método, ¿cómo hace para definir los valores de los parámetros? En el momento de hacer la llamada, se deben pasar tantos valores como parámetros está esperando el método. Esos valores pueden ser constantes (por ejemplo, 500), atributos del objeto que hace la llamada (por ejemplo, salario), parámetros del método desde el cual se hace la llamada (por ejemplo, pNuevoSalario), o expresiones que mezclen los tres anteriores (por ejemplo, salario + pNuevoSalario * 500 ).
  • ¿Cómo se hace la relación entre esos valores y los parámetros? Los valores se deben pasar teniendo en cuenta el orden en el que se declararon los parámetros. Eso se ilustra en la figura 1.15.

  • ¿Qué sucede si se pasan más (o menos) valores que parámetros? El compilador informa que hay un error en la llamada. Lo mismo sucede si los tipos de datos de los valores no coinciden con los tipos de datos de los parámetros.

Fig. 1.15 Llamada de un método con parámetros
  • Tenemos una clase C1, con un método m1() que tiene tres parámetros.
  • Tenemos una clase C2, con un atributo de la clase C1. Desde allí vamos a llamar el método m1() de la primera clase.
  • Debemos pasarle 3 valores en el momento de invocar el método. El primer valor es el parámetro x del método m2( ). El segundo valor es una expresión que incluye una constante y un atributo. El tercer valor es una constante de tipo cadena de caracteres.
  • Al hacer la llamada se hace la correspondencia uno a uno entre los valores y los parámetros.
  • Después de hacer la correspondencia se calcula cada valor y se le asigna al respectivo parámetro. Esta copia del valor se hace para todos los tipos simples de datos.
  • Una vez que se han inicializado los parámetros se inicia la ejecución del método.

6.10. Creación de Objetos

La creación de objetos es un tema que será abordado nuevamente en el segundo nivel. Sin embargo se explicará la creación de objetos porque es indispensable para entender la estructura de un programa completo. Para esto empezaremos contestando algunas preguntas.

  • ¿Quién crea los objetos del modelo del mundo? Típicamente, el proceso lo inicia la interfaz de usuario, creando una instancia de la clase más importante del modelo. Lo que sigue, depende del diseño que se haya hecho del programa.

  • ¿Cómo se guarda un objeto que acaba de ser creado? Más que guardar un objeto se debe hablar de referenciar. Una referencia a un objeto se puede guardar en cualquier atributo o variable del mismo tipo.

Un objeto se crea utilizando la instrucción new y dando el nombre de la clase de la cual va a ser una instancia. Para crear un empleado, por triángulo, se usa la expresión new Triangulo( ). Al ejecutar esta instrucción, el computador se encarga de buscar la declaración de la clase y asignar al objeto un nuevo espacio en memoria en donde pueda almacenar los valores de todos sus atributos. Como no es responsabilidad del computador darle un valor inicial a los atributos, éstos quedan en un valor que se puede considerar indefinido, tal como se sugiere en la figura 1.16, en donde se muestra la sintaxis de la instrucción new y el efecto de su uso.

Fig. 1.16 Creación de un objeto usando la instrucción new
  • El resultado de ejecutar la instrucción del ejemplo es un nuevo objeto, con sus atributos no inicializados.
  • Dicho objeto está "referenciado" por p, que puede ser un atributo o una variable de tipo Punto.

Para inicializar los valores de un objeto, las clases permiten la definición de métodos constructores, los cuales son invocados automáticamente en el momento de ejecutar la instrucción de creación. Un método constructor tiene dos reglas fundamentales:

  1. Se debe llamar igual que la clase.
  2. No puede tener ningún tipo de retorno, puesto que su único objetivo es dar un valor inicial a los atributos.

El siguiente es un ejemplo de un método constructor para la clase Punto:

public Punto(  )
{
    x = 0;
    y = 0;
}

Un método constructor tiene el mismo nombre de la clase (así lo puede localizar el compilador) y no tiene ningún tipo de retorno.

  • El método constructor del ejemplo le asigna valores iniciales por defecto a todos los atributos del objeto.
  • Un método constructor no se puede llamar directamente, sino que es invocado automáticamente cada vez que se crea un nuevo objeto de la clase.

El método constructor anterior le asigna un valor por defecto a cada uno de los atributos del objeto, evitando así tener valores indefinidos. El hecho de incluir este método constructor en la declaración de la clase hace que éste siempre se invoque como parte de la respuesta del computador a la instrucción new. En la figura1.17 se ilustra la creación de un objeto de una clase que tiene un método constructor.

Fig. 1.17 Creación de un objeto cuya clase tiene un método constructor.

Puesto que en muchos casos los valores por defecto no tienen sentido ( no todos los puntos pueden tener coordenadas 0,0 ), es posible agregar parámetros en el constructor, lo que obliga a todo aquel que quiera crear una nueva instancia de esa clase a definir dichos valores iniciales.

En el siguiente ejemplo, se muestra un constructor que recibe por parámetro las coordenadas que se desea asignar al punto desde su creación:

public Punto( double pX, double pY )
{
    x = pX;
    y = pY;
}
  • Este constructor exige 2 parámetros, de tipo real, para poder inicializar los objetos de la clase Punto.
  • En el constructor se asignan los valores de los parámetros a los atributos.

En la figura 1.18 se ilustra la creación de un objeto de una clase que usa el método constructor con parámetros definido arriba.

Fig. 1.18 Creación de un objeto a partir de un constructor con parámetros.
  • El objeto creado se ubica en alguna parte de la memoria del computador. Dicho objeto es referenciado por el atributo o la variable llamada "p".

Debido a que es necesario que el triángulo tenga 3 puntos, su método constructor debe incluir la creación de los 3 puntos, como se muestra a continuación:

public Triangulo(   )
{
    // Inicializa los puntos
    punto1 = new Punto( 200, 50 );
    punto2 = new Punto( 300, 200 );
    punto3 = new Punto( 100, 200 );
}

Tarea 11

Objetivo: Generar habilidad en el uso de los constructores de las clases.

Complete el constructor de la clase Color, de manera que reciba por parámetro los valores que se desea asignar a cada uno de sus atributos y los inicialice.

public Color(                                       )
{




}

Complete el constructor de la clase Triangulo para que inicialice el color de relleno y el color de las líneas, usando el constructor creado arriba. (Tenga en cuenta que los valores de cada componente del color se deben inicializar con un entero etre 0 y 255).

public Triangulo(   )
{
    // Inicializa los puntos
    punto1 = new Punto( 200, 50 );
    punto2 = new Punto( 300, 200 );
    punto3 = new Punto( 100, 200 );




}

results matching ""

    No results matching ""