11 . C a so d e E st u d i o 1 a P a r t e

25
11. Caso de Estudio 1a Parte Objetivos Describir cada etapa del proceso de desarrollo de software; diseñar una aplicación completa usando UML; implementar un diseño UML detallado en Java; documentar el código usando comentarios Javadoc ; distinguir entre pruebas unitarias y pruebas de integración ; probar unidades individuales del programa creando manejadores apropiados; documentar los resultados de prueba profesionalmente usando una bitácora de prueba . 11.1 Introducción El proceso de desarrollo de software requiere hacer varias tareas. Se pueden resumir como sigue: análisis y especificación : determinar qué requiere hacer el sistema (análisis) y escribirlo en una forma clara y no ambigua (especificación). diseño : tomar decisiones sobre cómo el sistema será construido para cumplir la especificación; implementación : convertir el diseño en un programa; probar : asegurar que el sistema ha sido implementado correctamente para cumplir la especificación original; instalación : enviar y configurar el sistema completo; operación y mantenimiento : ejecutar el sistema final y revisarlo de acuerdo a los requerimientos de cambio. En lugar de terminar por completo una tarea antes de comenzar la siguiente, los lenguajes orientados a objetos, como Java, fomentan que los sistemas se desarrollen poco a poco. Entonces, por ejemplo, se puede construir una clase y probarla (tal vez en presencia del cliente) antes de pasar a la siguiente, en lugar de esperar a que se desarrolle todo el sistema antes de probar e involucrar al cliente. En este capítulo y en el siguiente se muestra este proceso desarrollando un caso de estudio que permitirá darse una idea de cómo se puede desarrollar un 209

Transcript of 11 . C a so d e E st u d i o 1 a P a r t e

Page 1: 11 . C a so d e E st u d i o 1 a P a r t e

11. Caso de Estudio 1a Parte

Objetivos

● Describir cada etapa del proceso de desarrollo de software;

● diseñar una aplicación completa usando UML;

● implementar un diseño UML detallado en Java;

● documentar el código usando comentarios Javadoc ;

● distinguir entre pruebas unitarias y pruebas de integración ;

● probar unidades individuales del programa creando manejadores apropiados;

● documentar los resultados de prueba profesionalmente usando una bitácora de prueba .

11.1 Introducción

El proceso de desarrollo de software requiere hacer varias tareas. Se pueden resumir como sigue:

● análisis y especificación : determinar qué requiere hacer el sistema (análisis) y escribirlo en una forma clara y no ambigua (especificación).

● diseño : tomar decisiones sobre cómo el sistema será construido para cumplir la especificación;

● implementación : convertir el diseño en un programa; ● probar : asegurar que el sistema ha sido implementado correctamente para

cumplir la especificación original; ● instalación : enviar y configurar el sistema completo; ● operación y mantenimiento : ejecutar el sistema final y revisarlo de acuerdo

a los requerimientos de cambio.

En lugar de terminar por completo una tarea antes de comenzar la siguiente, los lenguajes orientados a objetos, como Java, fomentan que los sistemas se desarrollen poco a poco. Entonces, por ejemplo, se puede construir una clase y probarla (tal vez en presencia del cliente) antes de pasar a la siguiente, en lugar de esperar a que se desarrolle todo el sistema antes de probar e involucrar al cliente.

En este capítulo y en el siguiente se muestra este proceso desarrollando un caso de estudio que permitirá darse una idea de cómo se puede desarrollar un

209

Page 2: 11 . C a so d e E st u d i o 1 a P a r t e

sistema comercial desde cero; comenzando con una descripción informal de los requisitos, y luego especificando y diseñando el sistema usando notación UML y pseudocódigo cuando sea necesario. A partir de ahí se pasa a implementar el sistema en Java. Las aplicaciones Java, como esta, suelen constar de muchas clases que trabajan juntas. Cuando se prueba para encontrar errores, se comienza con un proceso de pruebas unitarias (pruebas a clases individuales) seguido de pruebas de integración (pruebas a clases que juntas forman una aplicación).

El sistema que se va a desarrollar mantendrá registros de los residentes de un albergue de estudiantes. Para facilitar la comprensión, se han simplificado las tareas, manteniendo los detalles de las personas al mínimo y manteniendo la funcionalidad básica; se tendrá la oportunidad de mejorar lo que se ha hecho en los ejercicios al final del próximo capítulo.

11.2 Especificación de requerimientos

La universidad local requiere un programa para administrar uno de sus albergues para estudiantes, que contiene varias habitaciones, cada una de las cuales puede ser ocupada por un solo inquilino que paga el alquiler mensualmente. El programa debe mantener una lista de inquilinos y sus pagos mensuales. La información guardada de cada inquilino consistirá en su nombre, su número de habitación y su lista de todos los pagos que el inquilino ha realizado (mes y monto) durante un año. El programa debe permitir al usuario agregar y eliminar inquilinos, mostrar una lista de todos los inquilinos, registrar un pago para un inquilino en particular y mostrar el historial de pagos de un inquilino.

11.3 El diseño

Las dos clases principales requeridas en esta aplicación son Inquilino (para guardar los detalles de un inquilino) y Pago (para guardar los detalles de un pago). Se han tomado una serie de decisiones de diseño sobre cómo se implementará el sistema, y se muestran enseguida:

● instancias de la clase Inquilino e instancias de la clase Pago serán guardadas en clases de colección separadas, ListaInquilinos y ListaPagos respectivamente;

● ambas clases colección, ListaInquilinos y ListaPagos , hacen uso de un ArrayList ;

● la clase Albergue , la cual guardará la ListaInquilinos , también actuará como la interfaz gráfica para el sistema.

210

Page 3: 11 . C a so d e E st u d i o 1 a P a r t e

En el diseño hay dos flechas de una clase a otra. En el diagrama UML estas representan asociaciones . Una asociación es un enlace de objetos de una clase a objetos de otra clase. Por ejemplo, un cliente podría tener una cuenta o más; un estudiante podría tener un tutor o más. La forma más simple de asociación es una relación uno a uno donde una sola instancia de una clase está asociada con una sola instancia de otra clase, por ejemplo una transacción de compra y un recibo . Ejemplos especiales de asociación son la herencia y el agregamiento.

Por ejemplo, las asociaciones representadas por las flechas son asociaciones uno a uno, un Inquilino requiere una sola instancia de una ListaPagos y un Albergue requiere una sola instancia de una ListaInquilinos .

211

Page 4: 11 . C a so d e E st u d i o 1 a P a r t e

La clase Albergue no ha sido diseñada y se hará hasta el siguiente capítulo donde se considera el diseño del sistema completo y prueba; por esta razón ha sido dibujada con línea discontinua, pero se puede considerar como una clase gráfica porque hereda desde la clase Application de JavaFX.

Para implementar la aplicación se debe comenzar con aquellas clases que no dependen de ninguna otra, para que puedan ser probadas unitariamente de forma aislada. Por ejemplo, no deberíamos comenzar implementando la clase Inquilino ya que requiere que la clase ListaPagos se implemente primero. Se puede ver en las asociaciones de la figura que la única clase que no requiere ninguna otra clase para su implementación es la clase Pago .

11.4 Implementación de la clase Pago

A lo largo de este caso de estudio, se hará uso del estilo de comentarios de Javadoc para documentar las clases. Se discutirá cómo leer y escribir comentarios de Javadoc con más detalle en la siguiente sección.

El código de la clase Pago está enseguida.

212

/** Clase usada para guardar los detalles de un pago en un albergue * @author Charatan y Kans * @version 27 octubre 2020 */ public class Pago { private String mes; private double importe;

/** Constructor que inicializa el pago mensual y el importe pagado * @param mesEn: mes de pago * @param importeEn: importe del pago */ public Pago(String mesEn, double importeEn) { mes = mesEn; importe = importeEn; }

/** Lee el mes del pago hecho. * @return Regresa el mes del pago hecho */ public String getMes() { return mes; }

/** Lee el importe pagado * @return Regresa el importe pagado */

Page 5: 11 . C a so d e E st u d i o 1 a P a r t e

Como se puede observar la clase Pago es simple y no requiere mucha explicación. Observar que se ha anulado el método toString() , por lo tanto se usa la etiqueta @Override , para dar una forma conveniente de imprimir un objeto Pago , como se discutió en el capítulo 9 .

Antes de incorporar esta clase en un programa más grande se debería probar si esta trabaja confiablemente. Probar una clase individualmente es frecuentemente referida como una prueba unitaria .

Para poder probar unitariamente la clase se requerirá implementar una clase separada especialmente para ese propósito. Esta clase nueva contendrá un método main() y actuará como un manejador para la clase original. Un manejador es un programa especial diseñado para ejecutar una clase particular. En los ejemplos previos, esta es la forma cómo se han probado las clases individuales. Inicialmente se deberá generar un objeto de la clase dada. Una vez que el objeto ha sido generado entonces se prueba el objeto llamando a sus métodos. Cuando se prueba la clase generando objetos y llamando sus métodos, se querrá mostrar resultados en la pantalla, tal como los datos guardados en el objeto. Se podría acceder estos datos llamando los métodos apropiados get() :

Este mostrará la siguiente salida: Mes: Enero Importe: 1250.0

Si bien puede ser necesario tener varias declaraciones de salida como esta en la aplicación final, es una forma bastante engorrosa de recuperar información de

213

public double getImporte() { return importe; }

@Override public String toString() { return "(" + mes + ", " + importe + ")"; } }

public class ProbadorPago { public static void main(String[] args) { Pago p1 = new Pago("Enero",1250); // código para mostrar datos del objeto System.out.println("Mes: "+p1.getMes()); System.out.println("Importe: "+p1.getImporte()); } }

Page 6: 11 . C a so d e E st u d i o 1 a P a r t e

un objeto durante la fase de prueba, cuando no se está tan preocupado por el formato de la salida, se puede hacer uso del método toString() que está en la clase Pago para mostrar todos los atributos de la clase en una sentencia print() o println() . Enseguida se muestra el probador modificado.

Al ejecutar la nueva versión el programa producirá el siguiente resultado: (Enero, 1250.0)

Se observa que el objeto Pago es mostrado en el formato dado en el método toString() . Se puede observar la utilidad del método toString() . A continuación se revisarán otras partes del sistema, para lo cual se requiere desarrollar dos tipos de lista, una ListaPagos y una ListaInquilinos . Ambas usan ArrayList para guardar sus colecciones respectivas. La ListaInquilinos requiere que la clase ListaPagos sea desarrollada primero, por lo tanto se inicia con esta.

11.5 Clase ListaPagos

El diseño de la clase ListaPagos es similar a la clase colección Banco que se mostró en el capítulo 8 . Ambas clases usan un ArrayList para guardar una colección de objetos. La diferencia principal entre las dos clases es que en la clase Banco el tipo contenido fue CuentaBancaria , mientras en la clase ListaPagos el tipo contenido es Pago . Se incluyó la constante MAX , para permitir registrar el número máximo de pagos que se quieren registrar en la lista. Como MAX es una constante se le ha dado visibilidad pública.

De acuerdo al diseño dado en la sección 11.3 en la clase ListaPagos , hay un constructor y métodos para agregar un pago a la lista, para revisar si la lista está llena y para tener la cantidad total de pagos hechos. También hay métodos para obtener un pago de acuerdo a una posición numérica y para calcular el total de los pagos hechos. Finalmente, hay un método toString() para facilitar la prueba. Enseguida el código de la clase ListaPagos en dónde se usa el estilo Javadoc de comentarios.

214

public class ProbadorPago { public static void main(String[] args) { Pago p1 = new Pago("Enero",1250); // crear objeto a probar System.out.println(p1); // llamada implícita a toString() } }

import java.util.ArrayList;

/** Clase colección para guardar una lista de objetos Pago * @author Charatan y Kans

Page 7: 11 . C a so d e E st u d i o 1 a P a r t e

215

* @version 27 octubre 2010 */ public class ListaPagos { // atributos private ArrayList<Pago> listaP; public final int MAX;

/** Constructor crea lista de pagos vacía y pone el tamaño máximo * @param max La cantidad máxima de pagos en la lista */ public ListaPagos(int max) { listaP = new ArrayList<>(); MAX = max; }

/** Revisa si la lista de pagos está llena * @return Regresa true si la lista está llena y false de otro modo */ public boolean isFull() { return listaP.size()== MAX; }

/** Obtiene la cantidad total de pagos * @return Regresa la cantidad total de pagos actuales en la lista */ public int getTotal() { return listaP.size(); }

/** Agrega un pago nuevo al final de la lista * @param pago El pago que será agregado * @return Regresa true si el objeto fue agregado exitosamente y * false de otra forma */ public boolean addPago(Pago pago) { if (!isFull()) { listaP.add(pago); return true; } else { return false; } }

/** Lee el pago en la posición dada en la lista * @param posicion La posición lógica del pago en la lista * @return Regresa el pago en la posición lógica dada en la lista * o nulo si no hay pago en esa posición lógica */ public Pago getPago(int posicion) {

Page 8: 11 . C a so d e E st u d i o 1 a P a r t e

Se observa al inicio del código que se importó la clase ArrayList del paquete java.util

Se usa la clase ArrayList para guardar una colección de pagos en el atributo listaP . Además de este atributo se tiene un valor constante public MAX para guardar la cantidad máxima de pagos que se pueden guardar:

El constructor inicializa el atributo ArrayList , listaP , y pone el valor para MAX vía el parámetro max enviado al constructor:

216

// revisar por una posición lógica válida if (posicion <1 || posicion > getTotal()) { // no hay objeto en la posición dada return null; } else { // restar uno a la posición lógica para obtener la posición // en el Arraylist return listaP.get(posicion - 1); } }

/** Calcular el total de los pagos hechos por el inquilino * @return Regresar el valor total de pagos registrados */ public double calcularPagoTotal() { double pagoTotal = 0; // inicializar pagoTotal // iterar en todos los pagos for (Pago p: listaP) { // agregar pago actual para total parcial pagoTotal = pagoTotal + p.getImporte(); } return pagoTotal; }

@Override public String toString() { return listaP.toString(); } }

import java.util.ArrayList;

private ArrayList<Pago> listaP; public final int MAX;

public ListaPagos(int max) { listaP = new ArrayList<>(); MAX = max; }

Page 9: 11 . C a so d e E st u d i o 1 a P a r t e

Se revisan enseguida los métodos. Primeramente, se tiene el método isFull() . listaP estará llena cuando su tamaño sea igual al valor de la constante MAX :

Se observa que el método size() de ArrayList ha sido usado para tener la cantidad de elementos actuales en listaP .

El método addPago() usa isFull() para revisar si hay espacio en listaP antes de agregar un objeto Pago nuevo. Un valor boolean es devuelto para indicar éxito o falla:

El método size() de ArrayList , que se comentó previamente, es usado otra vez para implementar el método getTotal() , el cual regresa la cantidad total de pagos hechos:

El método getPago() usa getTotal() para revisar la validez del parámetro posicion . El parámetro posicion es la posición lógica de un pago en la lista, el cual será un número entre 1 y la cantidad total de elementos en la lista. Si una posición inválida es mandada entonces el valor null es devuelto, de otra forma el pago en la posición asociada en el ArrayList es devuelta:

217

public boolean isFull() { return listaP.size()== MAX; }

public boolean addPago(Pago pago) { if (!isFull()) { listaP.add(pago); return true; } else { return false; } }

public int getTotal() { return listaP.size(); }

public Pago getPago(int posicion) { // revisar por una posición lógica válida if (posicion < 1 || posicion > getTotal()) { // no hay objeto en la posición dada return null; } else { // restar uno a la posición lógica para obtener la posición

Page 10: 11 . C a so d e E st u d i o 1 a P a r t e

El método calcularPagoTotal() hace la suma de todos los pagos en la lista. Se requiere usar un ciclo que recorra todos los elementos de la lista para que los importes de los pagos sean sumados a una variable local inicializada a cero. Para lo anterior se puede usar un ciclo for avanzado:

Finalmente, se anuló el método toString() otra vez para permitir que la lista de pagos sea mostrado como un String . Podría parecer una tarea retadora cuando se opera con una colección de objetos, pero la clase ArrayList tiene un método toString() incorporado para realizar esta tarea.

Si se supone que se tienen tres objetos pago en lista de pagos, como (Ene, 1250) , (Feb, 1200) y (Mar, 1250) , el método toString() del ArrayList regresa un String como la siguiente: [(Ene, 1250), (Feb, 1200), (Mar, 1250)]

Como se observa, el método toString() del ArrayList llama al método toString() de los elementos contenidos, los separa con comas y los encierra entre corchetes.

Se hará uso de este método toString() durante la prueba de la clase ListaPagos , pero antes se revisarán los comentarios Javadoc que han sido incluidos en la clase Pago y ListaPagos .

218

// en el Arraylist return listaP.get(posicion - 1); } }

public double calcularPagoTotal() { double pagoTotal = 0; // inicializar pagoTotal // iterar en todos los pagos for (Pago p: listaP) { // agregar pago actual para total parcial pagoTotal = pagoTotal + p.getImporte(); } return pagoTotal; }

@Override public String toString() { return listaP.toString(); }

Page 11: 11 . C a so d e E st u d i o 1 a P a r t e

11.5.1 Javadoc

Las herramientas de desarrollo Java de Oracle incluyen Javadoc , el cual permite generar documentación para clases en la forma de archivos HTML. Para poder usar esta herramienta se deben comentar las clases en el estilo Javadoc. En el capítulo 1 se menciona que los comentarios Javadoc deben iniciar con /** y terminar con */ . Los comentarios Javadoc pueden también contener etiquetas . Las etiquetas son marcadores especiales de formato que permiten registrar información tal como el autor de una pieza de código. En la siguiente tabla se dan algunas de las etiquetas usadas frecuentemente en comentarios Javadoc .

Las etiquetas @author y @version son usadas en los comentarios Javadoc para toda la clase. Se pueden ver ejemplos de estas etiquetas al inicio de la clase ListaPagos :

Cuando los comentarios Javadoc están en varias líneas, como en el ejemplo previo, es común, aunque no necesario, iniciar cada línea con un asterisco.

Las etiquetas @param y @return pueden ser usadas en los comentarios Javadoc precediendo cada método. La etiqueta @param es usada para nombrar y describir el propósito de un parámetro dado. La etiqueta @return es usada para describir el valor devuelto por un método. En el siguiente ejemplo están los comentarios Javadoc para el método addPago() , el cual usa las etiquetas @param y @return.

219

Etiqueta Descripción

@author El nombre o los nombres de los autores del código.

@version Número de versión del código, la fecha es usada frecuentemente.

@param Nombre de un parámetro y su descripción.

@return Una descripción del valor regresado de un método.

/** Clase colección para guardar una lista de objetos Pago * @author Charatan y Kans * @version 27 octubre 2010 */ public class ListaPagos { // atributos y métodos aquí }

/** Agrega un pago nuevo al final de la lista * @param pago: El pago que será agregado * @return Regresa true si el objeto fue agregado exitosamente

Page 12: 11 . C a so d e E st u d i o 1 a P a r t e

La etiqueta @param ha sido usada para proporcionar un comentario del parámetro ( pago ) y la etiqueta @return ha sido usada para comentar el uso del valor boolean devuelto por el método. Usando comentarios Javadoc de esta forma se proporciona una explicación comprensiva de la funcionalidad de una clase.

Los archivos de documentación HTML Javadoc pueden ser generados desde la línea de comandos usando el comando javadoc : javadoc ListaPagos.java o invocado directamente desde la IDE. La figura siguiente da una parte de la documentación generada como resultado de los comentarios Javadoc de la clase ListaPagos .

Los comentarios Javadoc agregados a la clase ListaPagos proporcionan una técnica para documentar el código escrito. La documentación es importante ya que ayuda en el mantenimiento del código, en caso de requerir alguna modificación futura. El código bien documentado también es más fácil de corregir si surgen errores durante el desarrollo. Además de comentar el código, se debe asegurar de que el código esté bien diseñado para que sea más fácil de leer y seguir.

220

* y false de otra forma */ public boolean addPago(Pago pago) { if (!isFull()) { listaP.add(pago); return true; } else { return false; } }

Page 13: 11 . C a so d e E st u d i o 1 a P a r t e

11.5.2 Diseño del código

La sangría consistente y clara es importante para mejorar la legibilidad de los programas. En los programas de ejemplo que se han presentado se han seguido dos reglas simples siempre:

● mantener alineada la línea que contiene la llave de apertura con la llave de cierre a la que pertenece;

● sangrar, en un nivel, todo el código dentro de las llaves.

Por ejemplo, el método addPago() de la clase ListaPagos :

221

Page 14: 11 . C a so d e E st u d i o 1 a P a r t e

11.6 Prueba de la clase ListaPago

Es importante probar las clases para asegurar que funcionan correctamente antes de continuar con el resto del desarrollo. Mientras la prueba de la clase Pago fue un ejemplo de prueba unitaria , probar la clase ListaPago es un ejemplo de prueba de integración porque la clase ListaPago trabaja conjuntamente con la clase Pago .

Para probar la clase ListaPago se necesita un manejador que no solamente cree un objeto ListaPago , pero que también se creen pagos para ser agregados a la lista.

Se tienen bastantes métodos para probar de la clase ListaPago , por lo que se debe dedicar un tiempo a considerar cómo se van a probar estos métodos. Por ejemplo, tendría sentido limitar el tamaño de ListaPago para que se pueda completar rápidamente la lista para revisar el método isFull() . Enseguida hay una posible estrategia de prueba:

1. limitar el tamaño de ListaPago a un número pequeño, por ejemplo cuatro, usando el constructor ListaPago ;

2. agregar dos pagos a esta lista, (Ene, 1250) y (Feb, 1200), usando el método addPago() ;

3. mostrar la lista con el método toString() para revisar que los elementos hayan sido agregados exitosamente;

4. revisar si el método isFull() regresa false ; 5. agregar dos pagos más a la lista, (Mar, 1250) y (Abr, 1235), usando el

método addPago() ; 6. repetir el punto 3.; 7. revisar si el método isFull() regresa true ;

222

Page 15: 11 . C a so d e E st u d i o 1 a P a r t e

8. obtener los detalles de uno de los pagos hechos, como el segundo pago, usando el método getPago() ;

9. intentar recuperar un pago en una posición no válida, por ejemplo la cinco;

10.mostrar el número total de pagos hechos con el método getTotal() ; 11. mostrar el total de los pagos hechos usando el método

calcularPagoTotal() ; 12. intentar agregar otro pago a la lista llena usando el método

addPago() ;

La estrategia deberá asegurar que todos los métodos de la clase sean probados y todas las rutas posibles a través de los métodos sean probadas. Así, por ejemplo, además de garantizar que se llame al método isFull() , se debe asegurar dar un escenario dónde el método debe devolver true y proporcionar un escenario dónde el método debe devolver false .

Teniendo definida la estrategia, los resultados de la prueba se deben anotar en un registro de pruebas . Un registro de pruebas es un documento con las pruebas que se realizaron durante el desarrollo del sistema. Cada renglón del registro de prueba asocia una entrada con una salida esperada. Si el resultado no es el esperado, las razones de este error se deben identificar y registrar en el registro.

La siguiente figura ilustra un registro de pruebas para documentar la estrategia de prueba desarrollada previamente.

223

Registro de Pruebas

Propósito: probar la clase ListaPagos

Número de ejecución: Fecha:

Acción Salida esperada Pasa/ Falla

Razón de falla

- Pedir el tamaño para la lista

Ingresar 4 Mostrar menú de opciones

Seleccionar AGREGAR Pedir detalles de pago

Ingresar Ene,1250 Mostrar menú de opciones

Seleccionar AGREGAR Pedir detalles de pago

Ingresar Feb,1200 Mostrar menú de opciones

Seleccionar MOSTRAR [(Ene,1250.0),(Feb,1200.0)] Mostrar menú de opciones

Seleccionar ESTA LLENO Lista NO está llena Mostrar menú de opciones

Page 16: 11 . C a so d e E st u d i o 1 a P a r t e

El programa ProbadorListaPagos es un manejador posible que se podría desarrollar para poder procesar las acciones en el registro de pruebas:

224

Seleccionar AGREGAR Pedir detalles de pago

Ingresar Mar,1250 Mostrar menú de opciones

Seleccionar AGREGAR Pedir detalles de pago

Ingresar Abr,1235 Mostrar menú de opciones

Seleccionar MOSTRAR [(Ene,1250.0),(Feb,1200.0), (Mar,1250.0),(Abr,1235.0)] Mostrar menú de opciones

Seleccionar ESTA LLENO Lista está llena Mostrar menú de opciones

Seleccionar OBTENER PAGO Pedir posición a recuperar

Ingresar 2 (Feb,1200.0) Mostrar menú de opciones

Seleccionar OBTENER PAGO Pedir posición a recuperar

Ingresar 5 NÚMERO DE PAGO NO VÁLIDO Mostrar menú de opciones

Seleccionar TOTAL DE PAGOS

4 Mostrar menú de opciones

Seleccionar TOTAL PAGADO 4935.0 Mostrar menú de opciones

Seleccionar AGREGAR Pedir detalles de pago

Ingresar May,1250 Error - lista llena Mostrar menú de opciones

Seleccionar SALIR Programa termina

public class ProbadorListaPagos { public static void main(String[] args) { char opcion; int tam; ListaPagos lista; // declarar referencia ListaPagos // obtener tamaño de la lista System.out.print("¿Tamaño de la lista? "); tam = ScannerFacil.nextInt(); lista = new ListaPagos(tam); // crear objecto a probar // menú do { // mostrar opciones System.out.println(); System.out.println("[1] AGREGAR");

Page 17: 11 . C a so d e E st u d i o 1 a P a r t e

225

System.out.println("[2] MOSTRAR"); System.out.println("[3] ESTÁ LLENO"); System.out.println("[4] OBTENER PAGO"); System.out.println("[5] TOTAL DE PAGOS"); System.out.println("[6] TOTAL PAGADO"); System.out.println("[7] SALIR"); System.out.println(); System.out.print("Ingresar una opción [1-7]: "); // obtener opción opcion = ScannerFacil.nextChar(); System.out.println(); // procesar opción switch(opcion) { case '1': option1(lista); break; case '2': option2(lista); break; case '3': option3(lista); break; case '4': option4(lista); break; case '5': option5(lista); break; case '6': option6(lista); break; case '7': System.out.println("PRUEBA COMPLETADA"); break; default: System.out.print("1-7 solamente"); } } while (opcion != '7'); } // AGREGAR static void option1(ListaPagos lista) { // pedir detalles del pago System.out.print("Ingresar mes: "); String mes = ScannerFacil.nextString(); System.out.print("Ingresar importe: "); double importe = ScannerFacil.nextDouble();

Page 18: 11 . C a so d e E st u d i o 1 a P a r t e

226

// crear objeto Pago con la entrada Pago p = new Pago(mes, importe); // intentar agregar pago a lista boolean ok = lista.addPago(p); // valor falso es regresado si no se puede agregar if (!ok) { // revisar si fue agregado System.out.println("ERROR: lista llena"); } } // MOSTRAR static void option2(ListaPagos lista) { System.out.println("ELEMENTOS INGRESADOS"); // llama a toString() de ListaPagos System.out.println(lista); } // ESTÁ LLENO static void option3(ListaPagos lista) { if (lista.isFull()) { System.out.println("Lista está llena"); } else { System.out.println("Lista no está llena"); } } // OBTENER PAGO static void option4(ListaPagos lista) { // pedir número de pago System.out.print("Ingresar número de pago a recuperar: "); int num = ScannerFacil.nextInt(); // recuperar objeto Pago de lista, posición no válida null Pago p = lista.getPago(num); if (p != null) { // revisar si se recuperó pago System.out.println(p); // llamar toString() de Pago } else { // error de posición no válida System.out.println("NÚMERO DE PAGO NO VÁLIDO"); } } // TOTAL DE PAGOS static void option5(ListaPagos lista) { System.out.print("NÚMERO TOTAL DE PAGOS INGRESADOS: "); System.out.println(lista.getTotal()); }

Page 19: 11 . C a so d e E st u d i o 1 a P a r t e

11.7 Implementación de la Clase Inqulino

De acuerdo al diagrama UML del diseño del sistema de la sección 11.3 , la clase Inquilino tiene cuatro atributos:

● nombre ; ● cuarto ; ● pagos ; ● MAX .

Los primeros dos representan el nombre y el cuarto del inquilino respectivamente. El tercer atributo, pagos , será implementado como un objeto ListaPagos y el último atributo, MAX , será implementado como un atributo de clase static . El atributo MAX también será implementado como una constante porque se asume que los inquilinos hacen un número fijo de pagos en un año, doce, uno por cada mes. Como las constantes de clase no pueden ser modificadas, tiene sentido permitir que estas sean declaradas como public . Enseguida el código para la clase Inquilino .

227

// TOTAL PAGADO static void option6(ListaPagos lista) { System.out.print("TOTAL DE PAGOS HECHOS HASTA AHORA: "); System.out.println(lista.calcularPagoTotal()); } }

/** Clase usada para registrar los detalles de un inquilino * @author Charatan y Kans * @version 31 octubre 2020 */ public class Inquilino { private String nombre; private int cuarto; private ListaPagos pagos; public static final int MAX = 12; /** Constructor inicializa el nombre y número de cuarto * del inquilino y crea lista de pagos vacía

* @param nombreEn Nombre del inquilino * @param cuartoEn Número de cuarto del inquilino

*/ public Inquilino(String nombreEn, int cuartoEn) { nombre = nombreEn; cuarto = cuartoEn; pagos = new ListaPagos(MAX); }

Page 20: 11 . C a so d e E st u d i o 1 a P a r t e

Los comentarios Javadoc deberán dar información suficiente para entender el código de la clase. Observar como los comentarios Javadoc para el constructor incluyen dos etiquetas @param , porque el constructor tiene dos parámetros. También se debe notar que el atributo pagos, tipo ListaPagos , puede usar cualquiera de los métodos ListaPagos revisados en la sección 11.5 . El método pagar() muestra lo anterior cuando se llama al método addPago() de ListaPagos .

11.8 Implementación de la Clase ListaInqulinos

La clase ListaInquilinos es una clase colección para guardar objetos Inquilino . Se usa nuevamente un ArrayList para guardar la colección y se tiene

228

/** Registra un pago del inquilino * @param pago Pago hecho por el inquilino */ public void pagar(Pago pago) { pagos.addPago(pago); // llamar método de ListaPagos } /** Lee el nombre del inquilino * @return Devuelve el nombre del inquilino */ public String getNombre() { return nombre; } /** Lee el número de cuarto del inquilino * @return Devuelve el cuarto del inquilino */ public int getCuarto() { return cuarto; } /** Lee los pagos del inquilino * @return Devuelve los pagos hechos por el inquilino */ public ListaPagos getPagos() { return pagos; } @Override public String toString() { return nombre+", "+cuarto +", "+pagos; } }

Page 21: 11 . C a so d e E st u d i o 1 a P a r t e

una constante MAX para fijar un límite superior de la cantidad de inquilinos que puede tener el albergue. Revisar el diseño de la clase ListaInquilinos de la sección 11.3 para recordar sus atributos y métodos.

La mayoría de los métodos de la clase ListaInquilinos deberá ser familiar gracias a la clase colección ListaPagos comentada previamente. Solo se revisarán dos métodos que no están presentes en la clase ListaPagos , los métodos removeInquilino() y search() . Se inicia con el método search() que de acuerdo al diagrama UML tiene la siguiente interfaz: search(int):Inquilino

El parámetro entero representa el número de cuarto del inquilino que el método está buscando. El inquilino regresado es el inquilino viviendo en ese cuarto particular; si no se encuentra inquilino en ese cuarto entonces null es devuelto. Enseguida se da un algoritmo adecuado para encontrar un inquilino, expresado en pseudocódigo:

Este algoritmo requiere un ciclo para buscar entre los inquilinos de la lista y un ciclo for avanzado es una elección buena para hacer esto:

Revisando enseguida el método removeInquilino() , se tiene del diagrama UML para este método la siguiente interfaz: removeInquilino(int):boolean

El parámetro entero representa el número de cuarto del inquilino que está siendo quitado de la lista y el valor boolean devuelto indica si el inquilino fue quitado o no.

229

CICLO DESDE primer inquilino HASTA último en la lista INICIO SI número de cuarto de inquilino actual = cuarto a encontrar INICIO REGRESAR inquilino actual FIN FIN

public Inquilino search(int cuarto) { // ciclo for avanzado para buscar en la lista de inquilino for (Inquilino actual : listaI) { // revisar si inquilino actual está en cuarto dado if (actual.getCuarto()==cuarto) { return actual; } } // no se encontró inquilino con número de cuarto dado return null; }

Page 22: 11 . C a so d e E st u d i o 1 a P a r t e

El método search() puede ser usado para determinar si un inquilino existe en un cuarto particular, un valor null será regresado si no hay inquilino. Si un inquilino está entonces puede ser quitado de la lista usando el método remove() de ArrayLista y un valor boolean de true puede ser devuelto, de otra forma false . Su código es:

El código completo para la clase ListaInquilinos se da enseguida. Los comentarios Javadoc proporcionados deberán dar explicación suficiente de cada parte de la clase.

230

public boolean removeInquilino(int cuarto) { Inquilino encontrarI = search(cuarto); // llamar search() if (encontrarI != null) { // revisar si hay inquilino en cuarto listaI.remove(encontrarI); // quitar inquilino return true; } else { return false; // no hay inquilino en ese cuarto } }

import java.util.ArrayList; /** Clase colección para guarda una lista de inquilinos * @author Charatan y Kans * @version 31 octubre 2020 */ public class ListaInquilinos { private ArrayList<Inquilino> listaI; public final int MAX; /** Constructor crea lista de inquilinos vacía y fija tamaño * máximo de la lista * @param max: La cantidad máxima de inquilinos en la lista */ public ListaInquilinos(int max) { listaI = new ArrayList<>(); MAX = max; } /** Agrega un inquilino nuevo al final de la lista * @param inquilino El inquilino que será agregado * @return Regresa true si inquilino agregado exitosamente y * false de otra forma */ public boolean addInquilino(Inquilino inquilino) { if (!isFull()) {

Page 23: 11 . C a so d e E st u d i o 1 a P a r t e

231

listaI.add(inquilino); return true; } else { return false; } } /** Quita el inquilino para el número de cuarto dado * @param cuarto El número de cuarto del inquilino a remover * @return Regresa true si inquilino fue quitado, * de otro modo false */ public boolean removeInquilino(int cuarto) { Inquilino encontrarI = search(cuarto); // llamar search() if (encontrarI != null) { // revisar si hay inquilino listaI.remove(encontrarI); // quitar inquilino return true; } else { return false; // no hay inquilino en ese cuarto } }

/** Busca el inquilino para el número de cuarto dado * @param cuarto El número de cuarto que se buscará * @return Regresa el inquilino en el cuarto dado, o null si * no hay inquilino */ public Inquilino search(int cuarto) { // ciclo for avanzado para buscar en la lista de inquilino for (Inquilino actual : listaI) { // revisar si inquilino actual está en cuarto dado if (actual.getCuarto()==cuarto) { return actual; } } // no se encontró inquilino con número de cuarto dado return null; }

/** Lee el inquilino en la posición dada en la lista * @param posicion Posición lógica del inquilino en la lista * @return Regresa inquilino en la posición lógica dada en la * lista o nulo si no hay pago en esa posición lógica */ public Inquilino getInquilino(int posicion) { // revisar por una posición lógica válida

Page 24: 11 . C a so d e E st u d i o 1 a P a r t e

Todo lo que falta para completar el caso de estudio en el capítulo siguiente es diseñar, implementar y probar la clase Albergue la cual no sólo registrará los inquilinos sino que también será la interfaz gráfica del usuario para el sistema.

232

if (posicion<1 || posicion>getTotal()) { // no hay objeto en la posición dada return null; } else { // restar 1 a la posición lógica para obtener // la posicion en el Arraylist return listaI.get(posicion - 1); } } /** Revisa si la lista está vacía * @return Regresa true si la lista está vacía, * false de otro modo */ public boolean isEmpty() { return listaI.isEmpty(); } /** Revisa si la lista de inquilinos está llena * @return Regresa true si la lista está llena, * false de otro modo */ public boolean isFull() { return listaI.size() == MAX; } /** Obtiene la cantidad total de inquilinos * @return Regresa la cantidad total de inquilinos actuales * en la lista */ public int getTotal() { return listaI.size(); } @Override public String toString() { return listaI.toString(); } }

Page 25: 11 . C a so d e E st u d i o 1 a P a r t e

11.9 Preguntas

1. El registro de pruebas de la sección 11.6 no incluye pruebas para los métodos getPago() y getTotal() de la clase ListaPagos. Tampoco incluye una revisión cuando se intenta quitar de una lista vacía. Modificar el registro de pruebas para incluir estas revisiones.

2. Desarrollar registros de pruebas para probar las clases Inquilino y ListaInquilinos .

3. Identificar los beneficios de agregar un método toString() en las clases y luego escribir un método toString() apropiado para la clase Banco del capítulo 8 .

233