![](/imgs/logo_uniandes.png)

ISIS1206 - Estructuras de Datos

Diseñando aplicaciones de consola

Antes de empezar: ¿Por qué aplicaciones de consola?

Este curso está centrado en el manejo de las estructuras de datos, los algoritmos existentes para manipularlas y otros algoritmos esenciales para la resolución de problemas como ordenar, buscar, etc. Es por esto que el enfoque principal estará en los algoritmos y se reducirá el esfuerzo dedicado a implementar una interfaz gráfica de usuario.

Patrón de diseño de aplicaciones basadas en línea de comandos(CLI)

Aunque ahora nuestras aplicaciones no tendrán una interfaz gráfica sino de consola, esto no quiere decir que los principios de diseño aprendidos en los cursos anteriores no sean válidos. Todo lo contrario, en el diseño de estas aplicaciones seguiremos manteniendo separada la implementación de la interfaz(interfaz) y la implementación de la clase principal de la aplicación(anteriormente mundo). La diferencia estará en que la interfaz será implementada utilizando la línea de comandos y no interfaz gráfica. Esto quiere decir que si nuestras aplicaciones son diseñadas de forma correcta será posible a futuro implementar una interfaz visual agregando una implementación en Swing o JavaFx sin tener que cambiar la implementación de la funcionalidad de la aplicación.

A continuación, es necesario definir un conjunto de reglas y procedimientos que permitan establecer un canal de comunicación entre un usuario y la aplicación final, considerando que esta opera bajo una interfaz de Línea de Comandos en una Terminal[1](#myfootnote1). Para ilustrar esto, se desea implementar una calculadora en consola que permita realizar las operaciones básicas sobre un conjunto de valores, e.g., sumar/dividir/restar/multiplicar/etc.

Para implementar nuestra calculadora utilizaremos dos clases Calculator y CalculatorUI. La clase Calculator implementa las operaciones básicas de una calculadora y es utilizada por la clase CalculadoraUI que implementa la interfaz de consola.

+----------------------+                        +--------------------------+
|                      |                        |                          |
|      Calculator      |                        |       CalculatorUI       |
|                      |                        |                          |
+----------------------+                        +--------------------------+
|                      |                        |                          |
|                      |<-----------------------|                          |
+----------------------+                        +--------------------------+
|                      |                        |                          |
+----------------------+                        +--------------------------+

A continuación se muestra el código de la clase Calculator que implementa las funciones básicas de una calculadora.

public class Calculator
{

   private double lastResult = 0;

   public double add(double a, double b){
      lastResult = a+b;
      return lastResult;
   }

   public double subtract(double a, double b){
      lastResult = a-b;
      return lastResult;
   }

   public double multiply(double a, double b){
      lastResult = a*b;
      return lastResult;
   }

   public double divide(double a, double b){
      lastResult = a/b;
      return lastResult;
   }

   public double getLasResult(){
     return lastResult;
   }
}

Diseño de la interfaz

Una interfaz de consola deberá realizar los siguientes pasos para manejar de forma correcta la interacción con el usuario final:

  • Presentar el menú
  • Leer la opción seleccionada por el usuario
    • Validar entradas
    • Convertir
  • Ejecutar la opción seleccionada
  • Imprimir el resultado en consola
    • Utilizar formato de java

Teniendo en cuenta este esquema se desea que el usuario acceda a un conjunto de opciones dispuestas por el programa (e.g., Salir del programa, Regresar al menú anterior, etc.), bajo la existencia de un sistema gráfico, esta función debería estar cubierta por parte de un conjunto de botones que permitan acceder a las diversas opciones. No obstante, en el presente caso, solo se cuenta con la entrada estándar como el único mecanismo de interacción con el usuario, sujeto a lo anterior, existen diversos métodos para capturar una opción seleccionada por el usuario de forma efectiva. Para nuestro caso, se implementará un procedimiento de selección de opciones inspirado en el menú de selección telefónico ofrecido por un sistema PBX. Considere el siguiente bloque de código:

   public class CalculadoraUI
   {

     ...

     public CalculatorUI(){
        calculator = new Calculator();
        sc = new Scanner(System.in);
     }

     private void show(){
        boolean finish = false;
        System.out.println("-----------------------------------------------");
        System.out.println("-                                             -");
        System.out.println("-            Simple Calculator                -");
        System.out.println("-                                             -");
        System.out.print  ("-----------------------------------------------");
        while(finish==false){
          try{
              showMenu();
              System.out.print("Select an option from the menu: ");
              // Read user selected option
              int opt = sc.nextInt();
              // Validate selected option
              if(opt>=1 && opt<=5){
                if(opt == EXIT_OPTION){
                  finish = true;
                } else {
                  handleOption(opt);
                }
              } else {
                System.out.println("Invalid Option. Check the menu and try again.");
              }
          } catch(InputMismatchException ex){
            sc.nextLine();
            System.out.println("The value you entered is invalid.");
          } catch(Exception ex){
            System.out.println("An error occurred while executing the operation. "+ex.getMessage());
          }
        }
     }
   }

Como es posible apreciar, se establece un lector(Scanner) sobre la entrada estándar cuando se crea una nueva instancia de la clase, este permite leer y restringir el tipo de entrada esperado por parte del usuario. En el método show inicia el ciclo principal del programa (El cual permite restringir y controlar el flujo de la aplicación) se procede a presentar el menú principal, luego se lee la opción seleccionada por el usuario y se valida. Si la opción es válida se delega al método handleOption, en caso contrario se imprime un mensaje para informar al usuario y continua el ciclo principal.

Excepciones: Es importante tener en cuenta el manejo de excepciones, en este caso se hace un manejo diferente para las excepciones de tipo InputMismatchException(las cuales son generadas cuando el usuario ingresa un valor inválido) y el resto de excepciones. Este manejo se debe a que cuando se genera la excepción InputMismatchException se debe descartar el valor ingresado por el usuario. Por ejemplo si se espera un entero utilizando el método nextInt pero el usuario ingresa el texto "uno" se generará la excepción y el valor ingresado por el usuario(uno) deberá ser descartado llamando el método nextLine().

Veamos ahora el detalle de los métodos showMenu y handleOption.

El método showMenu

Este método se encarga de imprimir el menú principal de la aplicación utilizando el flujo de salida System.out.

private void showMenu(){
  System.out.println();
  System.out.println("Main Menu: ");
  System.out.println("1. Add");
  System.out.println("2. Subtract");
  System.out.println("3. Multiply");
  System.out.println("4. Divide");
  System.out.println("5. Exit");
  System.out.println("-------------------------------------------------------");
}

El método handleOption : Interpretando la información ingresada por el usuario

Tras obtener una entrada por parte del usuario, es fundamental validarla y procesarla, de tal forma que el usuario no incumpla las instrucciones suministradas previamente. Para nuestro ejemplo, se presenta un menú que contiene cinco opciones, de las cuales 4 son opciones que invocan la funcionalidad de la calculadora, esto implica que a este método solo deben llegar opciones que se encuentren en el intervalo [1,4]. A continuación se presenta el procedimiento de verificación y procesamiento de entradas implementado en el método handleOption:

private void handleOption(int opt){
  System.out.print("Enter the first number:");
  int a = sc.nextInt();
  System.out.print("Enter the second number:");
  int b = sc.nextInt();
  switch(opt)
  {
    case 1:
      calculator.add(a,b);
      break;
    case 2:
      calculator.subtract(a,b);
      break;
    case 3:
      calculator.multiply(a,b);
      break;
    case 4:
      calculator.divide(a,b);
      break;
    default :
      throw new RuntimeException("Invalid Option. Check the menu and try again.");
  }
}
System.out.println("-------------------------------------------------------");
System.out.println(" ---> The result of the last operation is: "+calculator.getLasResult());
System.out.println("-------------------------------------------------------");

Este método se encarga de determinar la operación que desea invocar el usuario y llamar el método correspondiente sobre la clase Calculator, luego imprime el resultado en la consola. Tras validar y procesar la entrada del usuario, es posible continuar con la ejecución del programa, detenerla o reiniciarla, de acuerdo a los criterios de validación.

Si desea obtener más información y detalles de implementación puede descargar el proyecto de la calculadora en el siguiente enlace Calculadora. Para abrir el proyecto en DrJava, descomprima el archivo, luego haga clic en el menú principal Project->Open y seleccione el archivo project.xml que se encuentra en la carpeta descomprimida.


Tip: Existen muchas herramientas que le pueden ayudar si desea ser más creativo, por ejemplo puede convertir imágenes a texto con ascii-art-generator o generar títulos utilizando www.network-science.de/ascii

.__         .__          ____ ________ _______     ________
|__|  ______|__|  ______/_   |\_____  \\   _  \   /  _____/
|  | /  ___/|  | /  ___/ |   | /  ____//  /_\  \ /   __  \
|  | \___ \ |  | \___ \  |   |/       \\  \_/   \\  |__\  \
|__|/____  >|__|/____  > |___|\_______ \\_____  / \_____  /
         \/          \/               \/      \/        \/
                                             ,---,      ,----,     ,----..
  ,--,                 ,--,               ,`--.' |    .'   .' \   /   /   \      ,---.
,--.'|               ,--.'|              /    /  :  ,----,'    | /   .     :    /     \
|  |,      .--.--.   |  |,      .--.--. :    |.' '  |    :  .  ;.   /   ;.  \  /    / '
`--'_     /  /    '  `--'_     /  /    '`----':  |  ;    |.'  /.   ;   /  ` ; .    ' /
,' ,'|   |  :  /`./  ,' ,'|   |  :  /`./   '   ' ;  `----'/  ; ;   |  ; \ ; |'    / ;
'  | |   |  :  ;_    '  | |   |  :  ;_     |   | |    /  ;  /  |   :  | ; | '|   :  \
|  | :    \  \    `. |  | :    \  \    `.  '   : ;   ;  /  /-, .   |  ' ' ' :;   |   ``.  
'  : |__   `----.   \'  : |__   `----.   \ |   | '  /  /  /.`| '   ;  \; /  |'   ;      \
|  | '.'| /  /`--'  /|  | '.'| /  /`--'  / '   : |./__;      :  \   \  ',  / '   |  .\  |
;  :    ;'--'.     / ;  :    ;'--'.     /  ;   |.'|   :    .'    ;   :    /  |   :  ';  :
|  ,   /   `--'---'  |  ,   /   `--'---'   '---'  ;   | .'        \   \ .'    \   \    /  
 ---`-'               ---`-'                      `---'            `---`       `---`--`
          _____                    _____                    _____                    _____
         /\    \                  /\    \                  /\    \                  /\    \
        /::\    \                /::\    \                /::\    \                /::\    \
        \:::\    \              /::::\    \               \:::\    \              /::::\    \
         \:::\    \            /::::::\    \               \:::\    \            /::::::\    \
          \:::\    \          /:::/\:::\    \               \:::\    \          /:::/\:::\    \
           \:::\    \        /:::/__\:::\    \               \:::\    \        /:::/__\:::\    \
           /::::\    \       \:::\   \:::\    \              /::::\    \       \:::\   \:::\    \
  ____    /::::::\    \    ___\:::\   \:::\    \    ____    /::::::\    \    ___\:::\   \:::\    \  
 /\   \  /:::/\:::\    \  /\   \:::\   \:::\    \  /\   \  /:::/\:::\    \  /\   \:::\   \:::\    \
/::\   \/:::/  \:::\____\/::\   \:::\   \:::\____\/::\   \/:::/  \:::\____\/::\   \:::\   \:::\____\
\:::\  /:::/    \::/    /\:::\   \:::\   \::/    /\:::\  /:::/    \::/    /\:::\   \:::\   \::/    /
 \:::\/:::/    / \/____/  \:::\   \:::\   \/____/  \:::\/:::/    / \/____/  \:::\   \:::\   \/____/
  \::::::/    /            \:::\   \:::\    \       \::::::/    /            \:::\   \:::\    \
   \::::/____/              \:::\   \:::\____\       \::::/____/              \:::\   \:::\____\
    \:::\    \               \:::\  /:::/    /        \:::\    \               \:::\  /:::/    /
     \:::\    \               \:::\/:::/    /          \:::\    \               \:::\/:::/    /
      \:::\    \               \::::::/    /            \:::\    \               \::::::/    /
       \:::\____\               \::::/    /              \:::\____\               \::::/    /
        \::/    /                \::/    /                \::/    /                \::/    /
         \/____/                  \/____/                  \/____/                  \/____/
'####::'######::'####::'######:::::'##::::'#######::::'#####::::'#######::
. ##::'##... ##:. ##::'##... ##::'####:::'##.... ##::'##.. ##::'##.... ##:
: ##:: ##:::..::: ##:: ##:::..:::.. ##:::..::::: ##:'##:::: ##: ##::::..::
: ##::. ######::: ##::. ######::::: ##::::'#######:: ##:::: ##: ########::
: ##:::..... ##:: ##:::..... ##:::: ##:::'##:::::::: ##:::: ##: ##.... ##:
: ##::'##::: ##:: ##::'##::: ##:::: ##::: ##::::::::. ##:: ##:: ##:::: ##:
'####:. ######::'####:. ######:::'######: #########::. #####:::. #######::
....:::......:::....:::......::::......::.........::::.....:::::.......:::

1: Una interfaz basada en un entorno de Comando de Línea es preferida en los casos en los cuales, el dispositivo objetivo no cuenta con un entorno gráfico, e.g., Dispositivos Embebidos, así como dispositivos accedidos de forma remota i.e., SSH.