About this entry




Ejecución Condicional en Java (Conditional Executions in Java, Spanish only)

Los programas con instrucciones secuenciales son muy poco flexibles. La ejecución condicional que trabajaremos en este articulo nos permitirá que los objetos se comporten diferente al estar en diferentes circunstancias.

Liked it? !

Antes de leer este artículo se recomienda haber leído Procesamiento Numérico: Tipos de Datos.

Índice

  1. Ejecución condicional mas simple: if
  2. Enunciado switch
  3. Revisitando la entrada
  4. Tipo Booleano y Predicados
  5. Ejemplo

 

  1. Ejecución condicional mas simple: if

    1. Sintaxis

      El enunciado if tiene dos formas. La primera provee dos alternativas para ejecución. Si la condición es verdadera se ejecuta el cuerpo 1. Si la condición es falsa se ejecuta el cuerpo 2.

      if (condicion) cuerpo1 else cuerpo2

      La condición es una expresión booleana. Los cuerpos pueden ser enunciados simples o compuestos. Los enunciados compuestos los encerramos entre llaves.

      En la segunda forma podemos omitir la porción sobre la condición falsa. Lo utilizamos en el caso que no sea necesario ejecutar alguna acción cuando la condición es falsa. Si la condición es verdadera se ejecuta el enunciado. Si la condición es falsa no se lleva a cabo acción alguna.

      if (condicion) enunciado

      La condición debe de estar encerrada entre paréntesis.

       

    2. Modificación al Ejemplo del Sistema de Peaje

      Recordando un poco el ejemplo de Recoleccion de Peajes, en donde calculábamos la tarifa de los camiones de la siguiente forma:

      int tollDue = 5*axles + 10*(totalWeight/1000);

      Ahora supongamos que los requerimientos que nos da el Departamento de Carreteras nos dice que para los carros (vehículos de dos ejes) la tarifa es solamente de Q.5. El calculo de la tarifa para los camiones se mantiene igual. El calculo de la tarifa entonces quedaría:

      int tollDue; if (axles==2) tollDue=5; else tollDue = 5*axles + 10*(totalWeight/1000);

      De hecho este problema también se pudo haber resuelto sin usar el if, a través de la asignación condicional. Esta quedaría:

      int tollDue = (axles==2)?5:(5*axles + 10*(totalWeight/1000));

       

    3. Ejemplo completo: clase Empleado

      Modelar el sistema de planilla de una empresa para un conjunto de empleados pagados al destajo. El sistema debe de estar en la capacidad de calcular el pago del empleado en base a la tarifa que gana por hora y las horas trabajadas. Los empleados que trabajen mas de 40 horas recibirán el pago de las horas adicionales a 11/2 veces su tarifa por hora. Para prevenir el abuso de las horas extras, en el tiempo de el calculo del pago, si el empleado a trabajado 30 o mas horas extras en las ultimas dos semanas, se emitirá un mensaje de advertencia.

       

      1. Buscando el Objeto Primario

        Analizando los sustantivos del planteo del problema, tenemos: empleado, pago, tarifa, hora y hora extra. De estos el que parece ser mas importante es el Empleado, el cual es el que vamos a modelar.

         

      2. Definición del Comportamiento

        Del planteo del problema podemos deducir los siguientes comportamientos:

        • El calculo del pago para el Empleado
        • Obtener el nombre del empleado (para poder extraerlo en la planilla)

         

      3. Definiendo la Interfase

        Vamos a empezar con la declaración y creación de las instancias de la clase Empleado. Recordemos que la creación del objeto involucra la invocación del constructor y el proveer cualquier información que este necesite para la creación del objeto. Debemos de proveer el nombre del empleado para poder obtener dicho nombre mas adelante. De la misma forma debemos de proveer la tarifa que el empleado gana por hora que utilizaremos para calcular su pago. Sin embargo, las horas trabajadas varían entre un periodo y otro por lo que esta información no se puede proveer en el constructor. Nos gustaría crear un empleado de la siguiente forma entonces:

        Empleado e; e = new Empleado("Juan Perez", 40);

        De aquí deducimos que la clase se debe de llamar Empleado y el constructor debe recibir dos argumentos: una hilera (que representa el nombre) y un numero real (que representa la tarifa por hora).

        También debemos de incluir un método para que calcule y devuelva el pago. Este calculo requeriría el numero de horas trabajadas. Se podría hacer entonces:

        int pago; pago = e.calcPay(30);

        Necesitamos entonces un método calcularPaga que recibe un entero (el numero de horas trabajadas) y devuelve un real (el pago al empleado).

        Para obtener el nombre del empleado, lo podríamos hacer a través de un método getName. La impresión podría ser:

        System.out.println(e.getName());

        De aquí concluimos que el encabezado de los métodos que contendrá nuestra clase serán:

        public Empleado(String nombre, int tarifa) public int calcPay(short horas) public String getName()

         

      4. Programa de Prueba

        Un programa simple de prueba podría ser:

        import java.io.*; class ProbandoEmpleado { public static void main(String args[]) { Empleado e; e = new Empleado("Juan Perez",40); int pago; pago = e.calcPay((short)50); System.out.println(e.getName() + " gano la primera semana " + pago); pago = e.calcPay((short)62); System.out.println(e.getName() + " gano la segunda semana " + pago); } }

         

      5. Esqueleto

        Con la información recopilada en la interfase armamos el siguiente esqueleto.

        class Empleado { // aqui irian los atributos public Empleado(String nombre, float tarifa) { // enunciados } public int calcPay(short horas) { // enunciados } public String getName() { // enunciados } }

         

      6. Implementación

        Necesitamos almacenar sobre el empleado: su nombre y tarifa por hora. Además, para poder desplegar el mensaje de advertencia para los empleados que hayan trabajado mas de 30 horas en las ultimas dos semanas necesitaremos almacenar el numero de horas extras trabajas en la semana anterior. Almacenaremos esta información en los atributos: nombre, tarifa y horasExtrasAnt.

        Sin horas extras podemos calcular el sueldo de multiplicando el numero de horas extras por la tarifa por hora. Si un empleado ha trabajado horas extras necesitaremos pagar la parte del sueldo de las horas normales mas la parte del sueldo de la horas extras. La implementación quedaría así:

        class Empleado { private String nombre; private int tarifa; private short horasExtrasAnt; public Empleado(String nombre, int tarifa) { this.nombre = nombre; this.tarifa = tarifa; horasExtrasAnt = 0; } public int calcPay(short horas) { short horasExtrasAct; int pago; if (horas<=40) { horasExtrasAct=0; pago = (int)horas * tarifa; } else { horasExtrasAct=(short)(horas-40); pago = 40 * tarifa + (int)horasExtrasAct * tarifa; if (horasExtrasAnt + horasExtrasAct > 30) System.out.println("El empleado se esta exediendo en trabajo."); } horasExtrasAnt = horasExtrasAct; return pago; } public String getName() { return nombre; } }

         

      Ejercicios de clase

      1. Escribir una aplicación que utilice la la clase Empleado. La aplicación deberá preguntarle al usuario por el nombre del empleado y su tarifa por hora. A continuación se le preguntara al usuario por el numero de horas trabajadas para tres semanas. El programa deberá desplegar el sueldo de cada semana.
      2. Modifique la clase Empleado para proveer la capacidad de dar un aumento en el sueldo. El aumento deberá ser trabajado como un numero entre 1 y 100, que representa el porcentaje de aumento. Mas aun, cuando se le de un aumento a un empleado se debe de mandar un mensaje al System.out.
      3. Modifique el método calcPay para que pague el horas extras y doble horas extras. Esto es, horas trabajadas pasado 60 son pagadas al doble de la tarifa normal.
      4. Modifique la clase Estudiante (del tema anterior) para incluir un método que devuelva la nota en letras según la siguiente tabla:
        91-100 -> A
        81-90 -> B;
        71-80 -> C
        61-70 -> E
        0-59 -> F

     

  2. Enunciado switch

    1. Introducción

      Algunas veces necesitamos comparar una variable contra varios valores. Si hacemos esto con el if, este tendría siguiente forma:

      if (x==valor1) enunciado1 else if (x==valor2) enunciado2 else if (x==valor3) enunciado3 else enunciado4

      Java provee un estructura mas simple para hacer estas comparaciones, el enunciado switch. Este quedaría así:

      switch(x) { case valor1: enunciado1 break; case valor2: enunciado2 break; case valor3: enunciado3 break; default: enunciado4 }

      La expresión es evaluada y es comparada con los diferentes cases. Si encuentra una coincidencia se ejecuta la sentencia correspondiente. Si no encuentra una coincidencia se ejecuta la sentencia del default. Los valores del case deben ser constantes o literales. Cada case puede tener una secuencia de enunciados asociados a este sin necesidad de encerrarlos entre llaves. El default es opcional.

      Cada case debe de terminar con un break. Si el break es omitido la ejecución continua con los enunciados de los próximos case hasta el siguiente break o el fin del switch.

       

    2. Ejemplo

      Escribir un método que reciba el numero de mes de parámetro (1 = enero, 2 = febrero, ..., 12 = diciembre). El método deberá devolver el numero de días del mes. Asuma que no hay anos bisiestos.

      public byte diasMes(byte mes) { byte dias; switch(mes) { case 1: case 3: case 5: case 7: case 8: case 10: case 12: dias = (byte) 31; break; case 2: dias = (byte) 28; break; case 4: case 6: case 9: case 11: dias = (byte) 30; break; default: dias = 0; } return dias; }

       

  3. Revisitando la entrada

    1. El valor null

      Hemos trabajado con muchos métodos que devuelven referencias a objetos (toUpperCase, substring, muchos read que hemos escrito). A pesar de que muchas veces se devuelven referencias a objetos, algunas veces queremos devolver alguna indicación de que ningún objeto puede ser devuelto. Esto podría suceder con un método read que lea de un archivo que ya no tenga mas datos. Si no hay mas datos en el archivo esto quiere decir que ningún objeto puede ser construido.

      La palabra reservada null se utiliza exactamente para estos casos. null es una referencia a ningún objeto y puede ser asignada a cualquier referencia.

       

    2. Usando null en el método read

      El método readLine lee una línea desde un BufferedReader y devuelve una referencia a una instancia de la clase String que es creada a partir de esta línea. En el evento de que no hay mas datos de un archivo (condicion llamada fin de archivo) , readLine devuelve null en vez de un objeto String. Desearíamos devolver null desde nuestros métodos read en el caso que no hayan datos que leer.

      Escribiendo el método read para la clase Empleado de la forma en que lo hemos hecho nos quedaría así:

      public static Empleado read (BufferedReader br) throws Exception { String nombre = br.readLine(); int tarifa = Integer.parseInt(br.readLine()); return new Empleado(nombre, tarifa); }

      Modificaremos ahora el método de la siguiente forma: si readLine devuelve null (indicando fin del archivo) devolveremos null desde nuestro método.

      public static Empleado read (BufferedReader br) throws Exception { String nombre, tarifaStr; int tarifa; nombre = br.readLine(); if (nombre == null) return null; tarifaStr = br.readLine(); if (tarifaStr == null) return null; tarifa = Integer.parseInt(tarifaStr); return new Empleado(nombre, tarifa); }

    Ejercicio de clase

    Modifique el método read que elaboro para la clase Estudiante (en el tema anterior) para que que valide el fin del archivo de la lectura.

     

  4. Tipo Booleano y Predicados

    Así como las expresiones con operadores aritméticos resultan en un valor numérico, las expresiones con los operadores relacionales resultan en un valor booleano. A los resultados boolean los podemos asignar a variables booleanas, evaluarlos con operadores lógicos, pasarlos como argumentos a un método, devolverlos en un método y lo mas importante utilizarlos en la evaluación de una condición.

    Como podemos devolver un resultado booleano en los métodos y usarlo en la evaluación de una condición, entonces podemos agregar predicados a nuestras clases. Un predicado en programación es un método que devuelve un booleano (y por ende puede ser utilizado desde el if para la evaluación de la condición).

    Tomemos la comparación de hileras por ejemplo. Podríamos pensar que en el siguiente código:

    String s1, s2; s1 = "juan"; s2 = "juan"; if (s1 == s2) ...

    la comparación entre s1 y s2 resulte en verdadero. Recordemos que los que las variables de una clase representan no son los objetos, sino una referencia a los objetos. Dado que en el código anterior s1 y s2 están haciendo referencia a todos "juanes" distintos la comparación resulta en falso.

    Si por el otro lado tuviéramos la porción de código de la siguiente forma:

    String s1, s2; s1 = "juan"; s2 = s1; if (s1 == s2) ...

    El resultado de la comparación saldría verdadera, ya que ambos s1 y s2 hacen referencia al mismo "juan". Si lo que yo quisiera la hileras son iguales, a pesar de que fueran objetos distintos no lo hago a través del operador relacional de igualdad, sino a través del método equals que me provee la clase String. Este método recibe un String y lo compara con el String receptor. Si son equivalentes devuelve verdadero, de lo contrario devuelve falso. El bloque de código quedaría entonces:

    String s1, s2; s1 = "juan"; s2 = "juan"; if (s1.equals(s2)) ...

    La condición anterior se verificaría verdadera. El método equals es un ejemplo de un predicado.

    Ejercicio de clase

    Agréguele un predicado equals a la clase Nombre. El método deberá recibir un Nombre como parámetro y este se deberá comparar con el Nombre receptor. Si los nombres son equivalentes el método deberá devolver verdadero, de lo contrario deberá devolver falso.

     

  5. Segundo ejemplo: Clase Tiempo

    A continuación vamos a diseñar una clase que nos permita modelar y manipular valores que representen tiempos. Nos restringiremos a periodos de 24 horas, por lo que trabajaremos con horas y minutos solamente. Una clase tiempo no esta diseñada para ser utilizada por el usuario directamente. En vez de esto, la clase tiempo será utilizada por otras clases. A este tipo de clases se les llaman clases utilitarias.

     

    1. Objetos Primarios

      Los sustantivos principales del planteo del problema son hora del día, minutos y segundos. Siendo las horas y minutos parte del tiempo, concluimos que nuestra clase principal es el Tiempo y esta es la que modelaremos.

       

    2. Determinando el Comportamiento

      Hemos identificado las siguientes operaciones como útiles al trabajar con un objeto Tiempo:

      • Dada una duración, determinar el tiempo de terminación.
      • Comparar si ocurre antes de otro tiempo.
      • Comparar si ocurre después de otro tiempo.
      • Escribirlo a un PrintStream.

       

    3. Determinando la interfase

      Escribiendo una porción de código que utilice nuestra clase Tiempo:

      Tiempo t1 = new Tiempo(10, 15, "am"); Tiempo t2 = new Tiempo(3, 10, "pm"); Tiempo t3 = t2.addDuration(30); Tiempo t4 = t1.addDuration(3,30); if (t3.isBefore(t4)) t3.print(System.out); else if (t3.isAfter(t4)) t4.print(System.out); else System.out.print("nadie"); System.out.println(" llega antes.");

      Note que el método addDuration esta sobrecargado para permitir una duración solo en minutos o en horas y minutos. Como estamos utilizando los métodos isBefore e isAfter en una condición estos tendrían que devolver un booleano. Por simpleza, las horas y minutos se recibirán como int (para evitar el cast al utilizar las constantes literales).

       

    4. Esqueleto

      Recopilando la información anterior nos tenemos:

      class Tiempo { // aqui irian los atributos public Tiempo (int horas, int min, String amOrPm) { // enunciados } public Tiempo addDuration (int min) { // enunciados } public Tiempo addDuration (int horas, int min) { // enunciados } public boolean isBefore (Tiempo t) { // enunciados } public boolean isAfter (Tiempo t) { // enunciados } public void print (PrintStream ps) { // enunciados } }

       

    5. Determinando los atributos

      Una representación obvia para el tiempo seria tener tres atributos: horas, minutos y un valor que indique am o pm. Sin embargo lo obvio no siempre es lo mejor. Si nosotros representamos el tiempo como el numero de minutos transcurridos desde las 12:00am, las comparaciones entre los tiempos se harían mucho mas simples. La hora 3:20am la representaríamos como 200 (3*60 + 20). Por el otro lado, 3:20pm la representaríamos como 920 ( [3 +12]*60+20). Entonces solo necesitaríamos un atributo: minutosTotales.

       

    6. Implementando la clase

      Para la conversión de la hora para el total de minutos transcurridos desde las 12:00am, hay que tomar en cuenta cuatro casos:

      • La primera hora del día (de 12:00am hasta 12:59am)
      • El resto de la mañana (de 1:00am hasta 11:59am)
      • La hora a medio día (12:00pm hasta 12:59pm)
      • El resto de la tarde (1:00pm hasta 11:59pm)

      Bajo nuestra representación del tiempo, el método addDuration únicamente tiene que devolver un nuevo tiempo representado por los minutos totales desde las 12:00am con los minutos recibidos como parámetro agregados. Para construir de una manera simple dicho objeto incluimos un constructor que recibe  los minutos totales desde las 12:00am como parámetro. Dado que este constructor será utilizado para computo interno solamente (no forma parte de la interfase), se incluye como private.

      La implementación de isBefore e isAfter ahora es trivial, ya que solo comparamos el total de minutos desde las 12:00am del objeto receptor con el total de minutos desde las 12:00am del objeto recibido como parámetro.

      Por ultimo, la implementación del método print se hará a través de convertir el total de minutos desde las 12:00am a la representación habitual para poder imprimirlo al PrintStream. En esta conversión de nuevo se tienen que tomar en cuenta los cuatro casos mencionados anteriormente.

      import java.io.*; class Tiempo { private short minutosTotales; public Tiempo (int horas, int min, String amOrPm) { if (amOrPm.toLowerCase().equals("am")) { if (horas == 12) horas=0; } else if (horas <= 12) horas+=12; minutosTotales=(short) (horas*60 + min); } /** constructor utilizado para creaciones simples desde addDuration */ private Tiempo(short minutosTotales) { this.minutosTotales=minutosTotales; } public Tiempo addDuration (int min) { return new Tiempo((short) (minutosTotales + min)); } public Tiempo addDuration (int horas, int min) { return new Tiempo((short) (minutosTotales + horas*60 + min)); } public boolean isBefore (Tiempo t) { return this.minutosTotales < t.minutosTotales; } public boolean isAfter (Tiempo t) { return this.minutosTotales > t.minutosTotales; } public void print (PrintStream ps) { // %24 por si se pasa del dia int horas=(minutosTotales/60) % 24; int min=minutosTotales%60; boolean esTarde; if (horas>=12) { esTarde=true; horas-=12; } else esTarde=false; if (horas==0) horas=12; ps.print(horas + ":" + min); if (esTarde) ps.print(" pm"); else ps.print(" am"); } } 

    Ejercicios de clase

    1. Implemente la clase Tiempo usando tres atributos: uno para las horas, uno para los minutos y otro indicando si es am o pm.
    2. Modifique la clase Tiempo para que mantenga segundo también.
    3. Implemente la clase Fecha, para que incluya los siguientes comportamientos:
      1. Un constructor que reciba la fecha como una hilera en formato dd/mm/aaaa. El constructor debe de verificar si es una fecha valida (por ejemplo 31/4/1999 es una fecha invalida). Tome en cuenta los años bisiestos.
      2. Un predicado que devuelva verdadero si la fecha es valida y falso de lo contrario.
      3. La capacidad de comparar dos fechas distintas. Implemente los métodos isBefore e isAfter. Ambos de estos métodos deberán de recibir una fecha como parámetro (la cual será comparada con la fecha receptora). isBefore deberá devolver verdadero si la fecha receptora viene antes que la fecha recibida como parámetro. isAfter deberá devolver verdadero si la fecha receptora viene después que la fecha recibida como parámetro.

Siguiente articulo que se recomienda leer: Trabajando con Múltiples Objetos: Ciclos

Etiquetas de technorati:

Liked it? !

Posted on December 24th | 0 comments | Filed Under: Tutorial de Java