Documentar el código

Documentar el código

El código además de ser limpio, legible, entendible o estructurado entre otras características, debe estar muy bien documentado con el fin de efectuar un mantenimiento, modificación o ampliación de nuestra fuente. Además, es posible generar documentación en base a estos comentarios como es el caso de JavaDoc.

Existen muchas formas de comentar el código, por ejemplo:

C#, Java, PHP, etc.

  • Solo C#. Podemos hacerlo con tres barras /// en la que al crear una nueva línea, la siguiente línea queda también comentada con ///. Si las escribimos antes de una clase o un método, genera etiquetas de comentario que expondré a continuación.
  • Dos barras, crea una línea de código comentado
  • Comentario multilínea. Una barra y un asterisco /* provoca que el código esté comentado hasta que se cierre el comentario con asterisco barra */

Visual basic.NET

  • Comillas simples para comentar línea. ‘Esto es un comentario en VB.NET
  • Usar la etiqueta REM. REM Esto es un comentario en VB.NET
  • Si queremos crear varias líneas, debemos usar el concatenador guión bajo
    REM Esto es un comentario_ 
    en VB.NET

HTML

  • para cerrar el comentario

Comentarios de clases y métodos

Cuando estamos escribiendo código e instanciamos una clase o seleccionamos un método de una clase, los IDE,s nos muestran una ayuda que nos muestra que hace el método, que parámetros tiene o que sobrecargas y esto se consigue del siguiente modo:

C#

    • Escribimos /// precediendo a una clase o un método y se generan automáticamente una serie de etiquetas xml. La documentación de las etiquetas la pueden encontrar en el siguiente enlace.
      /// <summary>
      /// Obtiene el nombre completo del objeto Persona
      /// </summary>
      /// <param name="dni">DNI del objeto persona</param>
      /// <returns>Cadena de texto con el nombre completo</returns>
      public string GetFullName(string dni)
      {
      	return "Joaquín Martínez;
      }
      
      /// <summary>
      /// Obtiene el nombre completo del objeto Persona
      /// </summary>
      /// <param name="id">Identificador del objeto persona</param>
      /// <returns>Cadena de texto con el nombre completo</returns>
      public string GetFullName(int id)
      {
      	return "Joaquín Martínez;
      }
      

Java

    • Escribimos encima de un método /** e INTRO y se genera las etiquetas para documentar el código
      /**
      * Obtiene el nombre completo del objeto Persona
      * @param dni DNI del objeto persona
      * @return Cadena de texto con el nombre completo
      */
      public String getFullName(String dni){
      	return "Joaquín Martínez";
      }
      
      /**
      * Obtiene el nombre completo del objeto Persona
      * @param id Identificador del objeto persona
      * @return Cadena de texto con el nombre completo
      */
      public String getFullName(int id){
      	return "Joaquín Martínez";
      }
      

      coment04coment05

Regiones

Otra forma de mantener el código ordenado es mediante regiones, esto nos va a permitir por ejemplo mantener en una clase de negocio los métodos de actualización, adición y eliminación separados de los de selección, mantener propiedades en una región, campos en otra, métodos públicos en otra, privados en otra y así cuantas regiones queramos tener con el fin de mantener el código ordenado. Yo personalmente en una clase, generalizando, siempre tengo una región para constructores y destructores, una para enumeraciones, una para campos, una para métodos públicos y protected, otra para privados y si la clase es un form o window, añado una para controles y dentro de esta, por tipo de control, añado métodos de evento de cada uno, por ejemplo, Buttons, DataGrid, etc.

C#

#region Métodos públicos
        // aquí podemos poner todos los
        // miembros de clase que queramos
#endregion

VB.NET

#Region "Métodos Públicos"
' aquí podemos poner todos los
' miembros de clase que queramos
#End Region

def45-7-2-1
Java

// <editor-fold desc="Métodos públicos" defaultstate="collapsed">
        // aquí podemos poner todos los
        // miembros de clase que queramos
// </editor-fold>

coment06.png

Tareas

Visual Studio tiene comentarios de tareas, es decir si incluimos una serie de comentarios específicos (TOKEN) en alguna parte del código con un comentario adicional, en la lista de tareas, podemos acceder a esta localización de código para continuar con la tarea pendiente. Estos Token pueden personalizarse en el Menú Herramientas, Opciones, Lista de tareas. En este ejemplo, he colocado un TOKEN para revisar el contenido del método y en la lista de tareas, al hacer doble click, nos dirigiremos al código en cuestión.

coment07.png

Hay que añadir y es una buena costumbre, documentar las clases con autoría, fechas de creación y modificación, que hace la clase, que función tiene, las modificaciones que se han realizado, etc.

Hacedme caso, un poco tiempo perdido en documentar, nos hará ganar muchísimo tiempo en un futuro.

coment08.png

 

Anuncios

Atajos Netbeans

Atajos Netbeans

Todos los IDE,s tienen una serie de mejoras que nos ayudan a escribir código de manera fácil y evitando errores, en este caso Netbeans no podía ser menos.

¿Que son los atajos de Netbeans? Son palabras que nos permiten escribir bloques de código de una manera rápida evitando repetir estructuras continuamente. Lo primero de todo que son muy prácticos y lo segundo que evitan muchos errores, (causan otros), pero creo que las ventajas son muy superiores a los pocos inconvenientes que pudiera tener. Para el que no esté familiarizado con el código, lo mejor sería teclear todas las instrucciones letra por letra y olvidarse de los atajos por una temporada hasta que controléis la mayoría de las instrucciones. Hay dos tipos, uno mediante combinación de teclas y otros escribiendo palabras + TAB.

Las combinaciones de teclas, pueden visualizarse en los menús y menús contextuales. Alguno de los ejemplos de combinación de teclas:

  • CTRL + SPACE→ o completa la palabra que estamos intentando escribir o nos muestra una lista con las sugerencias.
  • ALT + ENTER→ Si nuestro código se encuentra subrayado en rojo, nos muestra que podemos hacer. Si instanciamos Scanner y no tenemos importado java.util.Scanner, al combinar sobre este nos mostrará lo mismo que si pulsamos sobre la bombilla de sugerencias.
  • CTRL + R→Imaginad que tenemos una variable en 20 sitios distintos, pues si nos situamos sobre ella y usamos este atajo, nos resalta todas las variables en el código pudiendo detectarlas con más facilidad.
  • ALT + SHIFT+ F →Da formato al código
  • CTRL + E → Borra líneas de código CTRL+ SHIFT+ Flecha Arriba o Abajo →Copia líneas de código
  • ALT + SHIFT+ Flecha Arriba o Abajo →Sube o baja líneas de código
  • CTRL + SHIFT + B. Navega hasta la fuente del código.
  • CTRL + B. Navega hasta la declaración
  • ALT + F7. Busca todas las referencias usadas en los proyectos abiertos
  • F6. Ejecuta el proyecto
  • CTRL + F5. Ejecuta en modo depuración

Ejemplos de palabras + TAB.

atajos01También es posible usar CTRL + SPACE cuando estemos escribiendo alguna de estas palabras y nos mostrará las sugerencias. Todos estos atajos están al principio de la lista marcados con un icono cuando pusalmos CTRL + SPACE

  • sout + TAB → System.out.println(“”)
  • soutv + TAB → System.out.println(“this = ” + this);
  • serr + TAB → System.err.println(“”);
  • for +TAB → for (int i = 0; i < 10; i++) { }
  • forc + TAB → for (Iterator iterator = col.iterator(); iterator.hasNext();) {Object next = iterator.next();}
  • fore + TAB → for (String arg : args) { }
  • whilexp +TAB → while (true) { }
  • do +TAB → do { } while (true);
  • if +TAB → if (true) { }
  • ifelse +TAB → if (true) { } else { }
  • sw +TAB → switch (var) { case val: break; default: throw new AssertionError(); }
  • trycatch → try { } catch (Exception e) { }

una vez que los escribimos, hay que tener en cuenta que algunos de ellos debemos completarlos (de ahí los posibles errores). Por ejemplo, sw que genera el bloque de código switch hay que escribir el valor de la variable y los valores de cada caso, si no el código nos daría error. El bloque de código fore, se debe indicar la colección o forc hay que indicar el iterator.

Espero que os sirva de ayuda aunque reitero que si no estáis familiarizados con el código hay que escribirlo mil veces hasta que nos suene antes de usar atajos.

Nomenclatura en la escritura de código

Nomenclatura en la escritura de código

Existen múltiples aspectos que hay que tener en cuenta en el desarrollo de software com pueda ser la planificación, el diseño de la aplicación, diseño de datos, la estructura de nombres de espacios, estructura de clases, los algoritmos, su eficiencia, la modularidad, por supuesto la abstracción, el encasulapmiento, la herencia, el polimorfismo, la limpieza en el código, el orden, la nomenclatura, etc.En este post, vamos a dedicarnos a ver la nomenclatura en los lenguajes de Java y .NET.

Y, ¿que es la nomenclantura? pues son los procedimientos empleados para nombrar los elementos del código y estos nombres empleados por convención Sun para Java y Microsoft para .NET recomiendan usar. El motivo por el cual debemos usar la convención recomendada nos va a permitir grandes ventajas en el futuro a la hora de modificar, ampliar o simplemente usar nuestras clases.

Para el nombramiento de elementos, utilizaremos un método llamado Camelcase o nomenclatura camello; esto quiere decir que escribiremos las palabras sin espacios y cada vez que se inicia una nueva palabra, se comienza en mayúsculas como por ejemplo, “EstoEsUnaPruebaDeCamelCase”. Además, existen dos tipos dependiendo si la primera letra comienza por mayúsculas (UpperCamelCase), o la primera letra comienza por minúsculas (lowerCamelCase).

Nos recomiendan también no usar la notación húngara en la que se nombran los tipos dependiendo de lo que sean añadiéndole un prefijo a cada objeto u otra recomendación es no simplificar o abreviar; si llamo a una propiedad PuedeVerseDeDia, no sería correcto llamarla PuedeVerDia o PVerDia. El procedimiento de nombrar el código de esta manera tan natural como si hablaramos, SalirPorLaPuertaPrincipal, le llamamos nomenclatura Pascal.

De todos estos elementos, también podemos decir que cada uno de estos elementos deben nombrarse del siguiente modo:

  • Espacios de nombres.  Para nombrar un espacio de nombres deberíamos usar un nombre que indique la funcionalidad que proporcionan los tipos incluidos en el espacio de nombres, por ejemplo si tuvieramos una serie de clases que supieran pescar o tuvieran algo que ver con la pesca, mi espacio de nombres se llamaría Waki.Pesca. UpperCamelCase para .NET y lowerCamelCase para Java (en Java se llaman package y agrupan clases.
  • Clases. Para las clases debemos usar nombres que identifiquen el objeto o sintagmas nominales, por ejemplo la clase Persona, Coche, Avion, DireccionIP, Electrodomestico, etc y para el caso de herencia, se aconseja que termine con el nombre de la clase que hereda, de modo que EmpleadoPersona, sería la clase Empleado que hereda de Persona o bien en .NET una clase que hereda de System.EventArgs finalizará con EventArgs o los que heredan de System.Exception que finalizan con Exception. Ahora bien, un objeto List o un ArrayList de la clase Persona debería llamarse personas para indicar que contiene objetos de este tipo aunque también se puede ver acabado con Collection como PersonasCollectionUpperCamelCase para .NET y Java
  • Interfaces. Para las interfaces se usaría el mismo método que las clases pero añadiéndoles una I al inicio del nombre y finalizando con “able” para indicar que un objeto que implementa una interfaz es capaz de realizar las funciones que la interfaz le dice que tiene que hacer. Si tenemos una clase que implementa la interfaz ISpeakable, esta debería hablar porque la interfaz así lo requiere.En .NET puedes encontrar interfaces con la letra I al inicio pero no se les añade able como sufijo como ICloneable e IComparable, IQueryable, simplemente se les trata igual que las clases, pero con la letra I, por ejemplo NotifyPropertyChanged o IAsyncResult. UpperCamelCase para .NET y Java
  • Métodos y funciones. Para los métodos debemos usar verbos o sintagmas verbales, por ejemplo avanzar, volver, venderCoche, añadirArticulo, etc. UpperCamelCase para .NET y lowerCamelCase para Java.
  • Variables y propiedades. Debemos nombralos con un sustantivo, sintagma nominal o un adjetivo, por ejemplo para nombrar la altura de un triángulo, podemos hacerlo con la propiedad Height o Altura, para el radio de una circunferencia Radio, etc. Las propiedades booleanas con un tru o false, es muy recomendable usar Is o Es, Can o Puede y Has o Tiene, por ejemplo si determinamos la propiedad de visibilidad de un componente podemos hacerlo con IsVisible, comprobar si una lista de objetos contiene estos HasObject, o si un objeto puede saltar CanJump Para el caso de .NET es muy recomendable usar propiedades en vez de variables de clase o campos. Las variables privadas comienzan por minúscula y en los parámetros aunque Microsoft no lo recomienda, comienzo los nombres con un guión bajo. si llamo al método Avanzar(50), el método lo escribo como
    
    void Avanzar(double _value){}.
    
    

    UpperCamelCase para .NET y lowerCamelCase para Java.

  • Constantes. Las constantes en Java son nombradas con mayúsculas y los espacios son sustituidos por el guión bajo, como CONSTANTE_GRAVITACIONAL. En .NET se tratan igual que las variables.
  • Eventos. Al igual que los métodos, usaremos verbos o sintagmas verbales pero haciendo alusión al tiempo presente y pasado. Aquí podemos poner como ejemplo de un objeto que salta y el evento se produce, cuando este comienza a saltar genera el evento Saltando o Jumping en ese mismo momento y Saltado o Jumped cuando ha dejado de hacerlo. En .NET si declaramos el evento EventHandler<T> este debería nombrarse acabando en EventHandler, por ejemplo si declaramos un cambio en el texto de una propiedad, podriamos escribir public event EventHandler<EventArgs> ChangedTextEventHandler mientras que para invocar el evento nombraríamos el método de invocación como OnChangedText.
  • Enumeraciones. Las nombraremos como las propiedades pero en plural, por ejemplo si creamos una enumeración de colores, la llamaremos Colores. (que original)

Otra cosa es ¿los escribo en inglés o español? pues como desees, yo escribo mi código en inglés porque es más universal (aunque el español también lo sea, el inglés es más, ya nos gustaría), pero lo que tiene que estar claro es que sigas las recomendaciones. Lo normal es que si creas una clase para que pueda ser usada por otras personas, los miembros públicos de la clase los escribiría en inglés, es decir cuando instancie ese objeto, reconozca y entienda el programador que quiere hacer o a que pertenece; el motor interior o miembros privados ya son cosa tuya, pero si mañana tienes que modificar o ampliar la clase y no has escrito y comentado correctamente el código mal vas a ir.

Bueno pues aún hay más, las bases de datos también tienen su nomenclatura, existen más tipos como delegados, recursos y otros, pero lo principal es el orden y el entendimiento. Espero que os sirva y seguid las recomendaciones.

Un saludo amig@s

Pd: He cambiado mi modo de escribir código muchas veces, pero siempre adaptándolo a las recomendaciones, no he reescrito lo anterior, pero lo nuevo si lo he hecho. Esto es un no parar.

ConsoleTools (2). Java

ConsoleTools (2). Java

En la segunda parte de las herramientas de consola, voy a exponeros un grupo de utilidades y herramientas de uso de archivos y serialización.En el grupo de utilidades disponemos de los siguientes métodos:

  • waiting.Método que realiza una espera controlada; lo utilizo cuando se visualiza un texto en la consola, se realiza una espera y se vuelve al control de programa. Este método contiene varias sobrecargas, una sin parámetros en la que se realzia una espera estándar de 3 segundos, otra con un parámetro de tiempo de espera, una más con tiempo de espera y mensaje inicial en pantalla y por último una con tres, tiempo de espera, mensaje inicial y mensaje final una vez agotado el tiempo. Creo que con estas sobrecargas están cubiertas nuestras necesidades.
        /**
         * Realiza una espera controlada de 3 segundos
         */
        public static void waiting(){
            waiting(3, "", "");
        }
    
        /**
         * Realiza una espera controlada
         * @param seconds Número de segundos de espera
         */
        public static void waiting(long seconds){
            waiting(seconds, "", "");
        }
    
         /**
         * Realiza una espera controlada
         * @param seconds Número de segundos de espera
         * @param initialMessage Mensaje visualizado antes de la espera
         */
        public static void waiting(long seconds, String initialMessage){
            waiting(seconds, initialMessage, "");
        }
    
        /**
         * Realiza una espera controlada
         * @param seconds Número de segundos de espera
         * @param initialMessage Mensaje visualizado antes de la espera
         * @param finalMessage Mensaje visualizado después de la espera
         */
        public static void waiting(long seconds, String initialMessage, String finalMessage){
    
            long initialTime=System.currentTimeMillis();
            long currentTime=0;
    
            System.out.println(initialMessage);
    
            do {
                currentTime=System.currentTimeMillis();
            } while (currentTime-initialTime<seconds*1000);
    
            System.out.println(finalMessage);
        }
    
  • keyDownToContinue. Este método visualiza en pantalla ‘Pulse INTRO para continuar’ y en realidad lo que hace es capturar una entrada vacía que no usa y nos permite continuar.
         /**
         * Solicita una pulsación de la tecla INTRO desde teclado para continuar
         * @throws IOException
         */
        public static void keyDownToContinue() throws IOException{
            Scanner in=new Scanner(System.in);
            System.out.println("Pulse INTRO para continuar...");
            in.nextLine();
        }
    
  • confirmationMessage. Mensaje que se visualiza en pantalla solicitando continuar o no mediante la pulsación de S o N devolviendo true o false.
        /**
         * Visualiza un mensaje a espera de confirmación S/N
         * @param message Mensaje que se visualiza
         * @return True para S o s. False para N o n.
         * @throws IOException
         */
        public static boolean confirmationMessage(String message) throws IOException{
            // Declaración de variables
            BufferedReader keyboardIn=new BufferedReader(new InputStreamReader(System.in));
            String textIn=null;
    
            boolean yes=false;
            boolean boolIn=false;
            System.out.println(message);
            do {
                System.out.println("\n¿Desea continuar?\nIntroduzca S o N");
                textIn = keyboardIn.readLine();
                boolIn=!"S".equals(textIn) && !"s".equals(textIn) && !"N".equals(textIn) && !"n".equals(textIn);
            } while (boolIn);
    
            if (textIn.equals("S") || textIn.equals("s")) {
                yes=true;
            }
    
            return yes;
        }
    
  • clearScreen. En las aplicaciones de consola, no podemos hacer una limpieza de la pantalla como tal, por lo que para hacer este proceso, simplemente añadimos una serie de líneas en blanco. Este método se encuentra swobrecargado dos veces con una inclusión de 20 líneas y otro donde se pueda indicar este número.
         /**
         * Borra la pantalla añadiendo lineas nuevas
         * Valor por defecto 20
         */
        public static void clearScreen(){
            clearScreen(20);
        }
    
        /**
         * Borra la pantalla añadiendo lineas nuevas
         * @param lines Número de líneas que se añadirán
         */
        public static void clearScreen(int lines){
            for (int i = 0; i < lines; i++) {
                System.out.println("");
            }
        }
    
  • getRandomInteger y getRandomDouble. Pocas explicaciones tienen estos métodos. Obtención de un número aleatorio entero u obtención de un número aleatorio doble.
         /**
         * Devuelve un valor aleatorio entre dos números
         * @param m Valor inferior
         * @param n Valor Superior
         * @return Número entero aleatorio
         */
        public static int getRandomInteger(int m, int n){
             return (int)(getRandomDouble(m,n));
        }
    
        /**
         * Devuelve un valor aleatorio entre dos números
         * @param m Valor inferior
         * @param n Valor Superior
         * @return Número double aleatorio
         */
        public static double getRandomDouble(int m, int n){
            double number=(Math.random()*(n-m+1)+m);
            return number;
        }
    
    
  • createMenu. Este método crea un menú en base a un array de texto que será en realidad el que contiene los items del menú. Esto sería el equivalente a un ComboBox con unos items y de los cuales escogemos uno de ellos. Tiene 3 sobrecargas, la primera solo pasa como parámetro el array de texto, la segunda además incluye un texto que se usará como cabecera de menú y por último uno más que añade el caracter utilizado como marco del menú, el cual en su defecto se usa un asterisco ‘*’. Mostramos el código y una muestra de como quedaría.
        /**
         * Crea un menú desde un array de cadenas con asterisco por defecto
         * @param arg Array String. Items del menú
         * @return Cadena de texto con menú de consola
         */
        public static String createMenu(String arg[]){
            return createMenu(arg,"*","");
        }
    
        /**
         * Crea un menú desde un array de cadenas
         * @param arg Array String. Items del menú
         * @param header Cabecera usada antes del menu
         * @return Cadena de texto con menú de consola
         */
        public static String createMenu(String arg[], String header){
            return createMenu(arg,"*",header);
        }
    
        /**
         * Crea un menú desde un array de cadenas
         * @param arg Array String. Items del menú
         * @param stringMenu Caracter utilizado para el marco del menú
         * @param header Cabecera usada antes del menu
         * @return Cadena de texto con menú de consola
         */
        public static String createMenu(String arg [], String stringMarcoMenu, String header){
    
            // Advertencias y validación
            if (stringMarcoMenu.length()>1){
                return "La cadena de menú debe contener solo un carácter.";
            }
            if (arg.length>99) {
                return "La longitud máxima del menú, debe ser inferior a 100";
            }
    
            // Declaración de variables
            int maxLong=0;
            String asterisk="";
            String textOut="";
    
            // Determinar la longitud máxima del texto
            for (int i = 0; i maxLong?arg[i].length():maxLong;
            }
    
            String [] temp=header.split("\\n");
            for (int i = 0; i maxLong?temp[i].length():maxLong;
            }
    
            // Cadena marco del menú
            // Sumo 10 para compensar los espacios, stringMenu, paréntesis y el número
    
            // crear el menú
            for (int i = 0; i < maxLong + 10; i++) {
                asterisk += stringMarcoMenu;
            }
    
            textOut +=asterisk + "\n";
    
            // cabecera si la hay
            textOut += header + dumpChar(" ", maxLong-header.length()) + "\n" + asterisk + "\n";
    
            for (int i = 0; i < arg.length; i++) {
                textOut += stringMarcoMenu + " (" + (i+1) + ") " + arg[i];
                for (int j = arg[i].length(); j <maxLong+3 ; j++) {
                    textOut += " ";
                }
                textOut += stringMarcoMenu+ "\n";
            }
            textOut +=asterisk + "\n";
            return textOut;
        }
    
    

    una muestra de como quedaría el menú del sistema planetario pasándole como parámetro el array String[] menu={"Calcular distancia entre dos objetos", "Calcular atracción gravitacional", "Calcular tiempo (línea recta)", "Listar objetos", "Salir"}; y como texto de cabecera ‘Sistema Solar’.

    
    **********************************************
    Sistema Solar
    **********************************************
    * (1) Calcular distancia entre dos objetos   *
    * (2) Calcular atracción gravitacional       *
    * (3) Calcular tiempo (línea recta)          *
    * (4) Listar objetos                         *
    * (5) Salir                                  *
    **********************************************
    
    Introduzca la opción...

Grupo de herramientas de archivos:

  • createFile.Pasando la ruta de un archivo, crea este.
  • createFolder.Pasando la ruta de la carpeta, crea esta.
  • existFile. Comprueba si un archivo existe en la ruta especificada
    /**
     * Crea un nuevo archivo
     * @param path Ruta del archivo
     * @throws IOException
     */
    public static void createFile(String path) throws IOException{

        try {
            File myFile=new File(path);
            if (myFile.createNewFile())
                System.out.println("El fichero se ha creado correctamente");
            else
                System.out.println("No ha podido ser creado el fichero");
        }
            catch (IOException ioe) {
                    ioe.printStackTrace();
                    }

    }

    /**
     * Crea una nueva carpeta
     * @param path Ruta de la carpeta
     * @throws IOException
     */
    public static void createFolder(String path) throws IOException{
        try
        {
            File myFile=new File(path);
            if (myFile.mkdir())
                System.out.println("La carpeta se ha creado correctamente");
            else
                System.out.println("No ha podido ser creada la carpeta");
        }
        catch (Exception ex){
            System.out.println(ex.getMessage());
        }
    }

    /**
     * Comprueba si existe un archivo
     * @param path Ruta del archivo
     * @return True si existe. False si no existe
     */
    public static boolean existFile(String path){
        File myFile=new File(path);
        return myFile.exists();
    }

Serialización. Por si alguien no sabe lo que es serialización, es guardar en un archivo, en memoria o en una base de datos, un objeto en bytes y deserializar es el proceso inverso, obtener un objeto desde un archivo, memoria o base de datos, de modo que podemos guardar objetos para luego extraerlos y usarlos de nuevo. Por tanto, los métodos de este grupo son serializar y deserializar:

    /**
     * Serializa un objeto en un archivo
     * @param iObject Objeto que se serializará
     * @param filePath Ruta del archivo
     */
    public static void serializar(Object iObject, String filePath){
        FileOutputStream fileOutputStream = null;
        ObjectOutputStream salidaSerializada = null;

        try {
            fileOutputStream = new FileOutputStream(filePath);
            salidaSerializada = new ObjectOutputStream(fileOutputStream);
            salidaSerializada.writeObject(iObject);  

            salidaSerializada.close();
            fileOutputStream.close();

        } catch (FileNotFoundException e) {
            System.out.println(e.getMessage());
        } catch (IOException e) {
            System.out.println(e.getMessage());
        } finally {
            try {
                if(fileOutputStream!=null) fileOutputStream.close();
                if(salidaSerializada!=null) salidaSerializada.close();
            } catch (IOException e) {
                System.out.println(e.getMessage());
            }
        }
    }

    /**
     * Retorna un objeto deserializado desde un archivo
     * @param filePath Ruta del archivo
     * @return Objeto deserializado
     * @throws IOException
     * @throws ClassNotFoundException
     */
    public static Object deserializar(String filePath) throws IOException, ClassNotFoundException{

        FileInputStream fileInputStream = null;
        ObjectInputStream entradaSerializada = null;
	Object iObject=null;
        try {
            fileInputStream=new FileInputStream(filePath);
            entradaSerializada=new ObjectInputStream(fileInputStream);

            iObject=entradaSerializada.readObject();
            entradaSerializada.close();
            fileInputStream.close();
            return iObject;
        } catch (FileNotFoundException e) {
            System.out.println(e.getMessage());
        } catch (IOException e) {
            System.out.println(e.getMessage());
        } finally {
            try {
                if(fileInputStream!=null) fileInputStream.close();
                if(entradaSerializada!=null) entradaSerializada.close();
            } catch (IOException e) {
                System.out.println(e.getMessage());
            }
	    return iObject;
        }
    }

Pues ya que tenemos nuestras herramientas,os muestro el método utilizado para crear el menú en el sistema planetario el cual crea el menú, entra en un bucle do while y dentro de este se encuentra una instrucción switch con las opciones que podría seleccionar el usario y dentro de cada caso, las acciones a realizar, las cuales utilizan muchos de los métodos incluidos en esta clase.

Saludos “der Waki”

    private static void menuPrincipal() throws IOException, ParseException{
        // DECLARACIÓN DE VARIABLES
        String opcionMenu = "";
        String[] menu={"Calcular distancia entre dos objetos", "Calcular atracción gravitacional",
            "Calcular tiempo (línea recta)", "Listar objetos", "Salir"};

        // MENU, INICIO Y FIN
        do {
            System.out.println(ConsoleUtilities.createMenu(menu,sl.nombre));
            opcionMenu = String.valueOf(ConsoleUtilities.readText("Introduzca la opción..."));

            switch (opcionMenu) {
                case "1":
                    // Calcular distancia entre objetos
                    String str1 = ConsoleUtilities.readText("Introduzca el nombre del primer objeto...");
                    String str2="";
                    ObjetoSistema obj1=sl.getObjeto(str1);
                    ObjetoSistema obj2=null;
                    if (obj1==null) {
                        System.out.println("El objeto no existe.");
                    }
                    else {
                        str2 = ConsoleUtilities.readText("Introduzca el nombre del segundo objeto...");
                        obj2=sl.getObjeto(str2);
                        if (obj2==null) {
                            System.out.println("El objeto no existe.");
                        }
                        else {
                            System.out.println(ConsoleUtilities.toNumber(obj1.calcularDistancia(obj2)/1000) + " Km");
                        }
                    }
                    ConsoleUtilities.keyDownToContinue();
                    break;
                case "2":
                    // Calcular atracción gravitacional entre dos objetos
                    String str3 = ConsoleUtilities.readText("Introduzca el nombre del primer objeto...");
                    String str4="";
                    ObjetoSistema obj3=sl.getObjeto(str3);
                    ObjetoSistema obj4=null;
                    if (obj3==null) {
                        System.out.println("El objeto no existe.");
                    }
                    else {
                        str4 = ConsoleUtilities.readText("Introduzca el nombre del segundo objeto...");
                        obj4=sl.getObjeto(str4);
                        if (obj4==null) {
                            System.out.println("El objeto no existe.");
                        }
                        else {
                            double calculo=obj3.getAtraccion(obj4);
                            System.out.println(ConsoleUtilities.toNumber(calculo) + " Newton");
                        }
                    }
                    ConsoleUtilities.keyDownToContinue();
                    break;

                case "3":
                    // Calcular velocidad
                    String str5 = ConsoleUtilities.readText("Introduzca el nombre del primer objeto...");
                    String str6="";
                    ObjetoSistema obj5=sl.getObjeto(str5);
                    ObjetoSistema obj6=null;
                    if (obj5==null) {
                        System.out.println("El objeto no existe.");
                    }
                    else {
                        str6 = ConsoleUtilities.readText("Introduzca el nombre del segundo objeto...");
                        obj6=sl.getObjeto(str6);
                        if (obj6==null) {
                            System.out.println("El objeto no existe.");
                        }
                        else {
                            double calculoKm=obj5.calcularDistancia(obj6)/1000;
                            double velocidad = ConsoleUtilities.readDouble("Introduzca la velocidad km/h (Si pulsa INTRO, se asignará 28000 km/h... ",28000);
                            if (velocidad>0){
                                double tiempo=(calculoKm / velocidad)/(24);

                                System.out.printf("Invertirá para recorrer %s Km a una velocidad de %s Km/h, %s días\n",
                                        ConsoleUtilities.toNumber(calculoKm),ConsoleUtilities.toNumber(velocidad), ConsoleUtilities.convertToTime(tiempo*60*60*24));

                            }
                            else {
                                System.out.println("Velocidad no válida");
                            }
                        }
                    }
                    ConsoleUtilities.keyDownToContinue();
                    break;
                case "4":
                    // Listar objetos
                    menuListado();
                    break;
            }

        } while (!opcionMenu.equals("5"));

        ConsoleUtilities.clearScreen();
        System.out.println("La aplicación ha finalizado");
    }

ConsoleTools (1). Java

ConsoleTools (1). Java

Como ya os comenté en un post anterior, es importante que vayamos creando nuestras clases de código reutilizable, como por ejemplo en este caso os voy a mostrar una clase de herramientas y utilidades que uso para aplicaciones de consola en Java. Como las aplicaciones de consola suelen ser eso, texto puro en consola, a veces es necesario obtener claridad en los textos o en otro caso simplificar acciones que comunmente realizamos, para ello, inlcuyo dentro de la clase una serie de métodos estáticos que me van a permitir crear más fácil y rápido mis aplicaciones.

Dentro de la clase he creado distintos grupos de código, lo primero para aclararme y no repetir métodos o saber que es lo que tengo y en segundo lugar cuando el número de métodos es grande, es conveniente haber ordenado y agrupado nuestro código. A continuación expongo los grupos y método a método.

  • Solicitudes desde teclado. En una aplicación de consola es normal solicitar datos desde teclado ya que no tenemos controles TextBox, ComboBox ni nada por el estilo, por tanto tenemos que ingeniarnos un método fácil y práctico para solicitar información desde el teclado y pasarla a la aplicación. En este caso, he creado varios métodos estáticos y sus sobrecargas para obtener texto, obtener números, textos o fechas.
    • Lectura de texto. En este caso, existen dos métodos de lectura de texto, uno sobrecargado para leer texto y otro para leer varias veces el texto y devolver un array.readText es un método al cual se le pasan dos parámetros, el primero es el texto que se muestra por pantalla al solicitar el texto y el segundo parámetro, un valor booleano que permite si el valor que se captura desde el teclado puede ser un elemento vacío. El método crea un objeto BufferedReader, muestra por pantalla el textoy entra en un bucle do... while hasta obtener el texto. Una vez capturado desde el teclado lo devuelve. readText tiene otro método sobrecargado con un solo parámetro enviando el parámetro allowEmpty con un false. Por último el método readArrayText solicita mediante el método readText tantas cadenas de texto como necesite el usuario devolviendo una array de cadena.( Lo he limitado a 8 cadenas como podía haberlo hecho a 100). Un ejemplo. String stringFromKeyboard = ConsoleUtilities.readText("Introduzca el nombre del usuario...");, solicitando por pantalla el texto del primer parámetro y asignando e la variable stringFromKeyboard el valor obtenido desde el teclado.
          /**
          * Devuelve un valor desde el teclado
          * @param textIn Texto que se visualiza en la consola
          * @return Texto procedente del teclado
          * @throws IOException
          */
          public static String readText(String textIn, boolean allowEmpty) throws IOException{
      
              // Declaración de variables
              BufferedReader keyboardIn=new BufferedReader(new InputStreamReader(System.in));
              String textOut=null;
      
              // Imprimir texto de consola
              System.out.println(textIn);
      
              // Solicitar datos desde teclado
      
              do {
                  textOut=keyboardIn.readLine();
              } while (ConsoleTools.isNullOrEmpty(textOut) &amp;&amp; !allowEmpty);
      
              // Retornar texto
              return textOut;
          }
      
          /**
          * Devuelve un valor desde el teclado
          * @param textIn Texto que se visualiza en la consola
          * @return Texto procedente del teclado
          * @throws IOException
          */
          public static String readText(String textIn) throws IOException{
              return ConsoleTools.readText(textIn, false);
          }
      
          /**
          * Devuelve un array de textos. Limitado a 8 textos
          * @param textIn Texto que se visualiza en la consola
          * @return Array de texto
          */
          public static String[] readArrayText(String textIn) throws IOException{
              int n=0;
              int indexMax=8;
      
              String[] texts=new  String[indexMax];
      
              do {
                  texts[n]=ConsoleTools.readText(textIn);
                  n++;
              } while (ConsoleTools.confirmationMessage("¿Desea introducir más textos?") || n==indexMax);
      
              return texts;
      
          }
      
    • Lectura de números. Para obtener números desde teclado y asegurarnos que estos son lo que son y del tipo que son, uso un método sobrecargado para leer números enteros, otro para enteros positivos y por último otro sobrecargado para leer números con precisión doble. En todos ellos, se validan los datos comprobando que son del tipo que se solicita y que realmente son números y no otro objeto.
      Existe un método sobrecargado para lectura de números enteros, uno que tiene dos parámetros readInteger(String textIn, int defaultValue)pasando una cadena que se visualizará por pantalla y otro que pasará un valor por defecto en caso de no introducir ningún dato y otro método readInteger(String textIn)que solo pasa la cadena. Otro método para enteros es readPositiveInteger(String textIn) el cual además ed validar que es numérico y entero, comprueba que es positivo.
      Para lectura de números con precisión doble, disponemos de dos métodos sobrecargados igualmente que para números enteros.

          /**
          * Devuelve un valor entero desde el teclado
          * @param textIn Texto que se visualiza en la consola
          * @return Número procedente desde el teclado
          */
          public static int readInteger(String textIn) throws IOException{
              // Declaración de variables
              BufferedReader keyboardIn=new BufferedReader(new InputStreamReader(System.in));
              int numberOut=0;
              String numberOutToString=null;
              boolean validate=false;
      
              // Imprimir texto de consola
              System.out.println(textIn );
      
              // Solicitar datos desde teclado
              do {
                  numberOutToString=keyboardIn.readLine();
                  validate=ConsoleTools.isInteger(numberOutToString);
                  // Mensaje de Validación errónea
                  if (!validate) {
                      System.out.println("No es un valor válido. Introduzca un número entero.");
                  }
              } while (!validate);
      
              numberOut=Integer.parseInt(numberOutToString);
              // Retornar número
              return numberOut;
          }
      
          public static int readInteger(String textIn, int defaultValue) throws IOException{
              // Declaración de variables
              BufferedReader keyboardIn=new BufferedReader(new InputStreamReader(System.in));
              int numberOut=0;
              String numberOutToString=null;
              boolean validate=false;
      
              // Imprimir texto de consola
              System.out.println(textIn );
      
              // Solicitar datos desde teclado
              do {
                  numberOutToString=keyboardIn.readLine();
                  validate=ConsoleTools.isInteger(numberOutToString);
      
                  if (!validate) {
                      numberOutToString=String.valueOf(defaultValue);
                      validate=true;
                  }
              } while (!validate);
      
              numberOut=Integer.parseInt(numberOutToString);
              // Retornar número
              return numberOut;
          }
      
          /**
          * Devuelve un valor entero y positivo desde el teclado
          * @param textIn Texto que se visualiza en la consola
          * @return Número procedente desde el teclado
          */
          public static int readPositiveInteger(String textIn) throws IOException{
              // Declaración de variables
              BufferedReader keyboardIn=new BufferedReader(new InputStreamReader(System.in));
              int numberOut=0;
              String numberOutToString=null;
              boolean validate=false;
      
              // Imprimir texto de consola
              System.out.println(textIn );
      
              // Solicitar datos desde teclado
              do {
                  numberOutToString=keyboardIn.readLine();
                  validate=ConsoleTools.isPositiveInteger(numberOutToString);
                  // Mensaje de Validación errónea
                  if (!validate) {
                      System.out.println("No es un valor válido. Introduzca un número entero.");
                  }
              } while (!validate);
      
              numberOut=Integer.parseInt(numberOutToString);
              // Retornar número
              return numberOut;
          }
      
          /**
           * Devuelve un valor entero desde el teclado
           * @param textIn Texto que se visualiza en la consola
           * @return Número procedente desde el teclado
           * @throws java.io.IOException
           */
          public static double readDouble(String textIn) throws IOException{
              // Declaración de variables
              BufferedReader keyboardIn=new BufferedReader(new InputStreamReader(System.in));
              double numberOut=0;
              String numberOutToString=null;
              boolean validate=false;
      
              // Imprimir texto de consola
              System.out.println(textIn );
      
              // Solicitar datos desde teclado
              do {
                  numberOutToString=keyboardIn.readLine();
                  validate=ConsoleTools.isNumeric(numberOutToString);
                  // Mensaje de Validación errónea
                  if (!validate) {
                      System.out.println("No es un valor válido. Introduzca un número con doble precisión.");
                  }
              } while (!validate);
      
              numberOut=Double.parseDouble(numberOutToString);
              // Retornar número
              return numberOut;
          }
      
          /**
           * Devuelve un valor entero desde el teclado
           * @param textIn Texto que se visualiza en la consola
           * @param defaultValue Valor por defecto que se asigna en caso de no validez de entrada
           * @return Número procedente desde el teclado o valor por defecto
           * @throws IOException
           */
          public static double readDouble(String textIn, double defaultValue) throws IOException{
              // Declaración de variables
              BufferedReader keyboardIn=new BufferedReader(new InputStreamReader(System.in));
              double numberOut=0;
              String numberOutToString=null;
              boolean validate=false;
      
              // Imprimir texto de consola
              System.out.println(textIn );
              // Solicitar datos desde teclado
              do {
                  numberOutToString=keyboardIn.readLine();
                  validate=ConsoleTools.isNumeric(numberOutToString);
                  // Mensaje de Validación errónea
                  if (!validate) {
                      numberOutToString=String.valueOf(defaultValue);
                      validate=true;
                  }
              } while (!validate);
      
              numberOut=Double.parseDouble(numberOutToString);
      
              // Retornar número
              return numberOut;
          }
      
    • Lectura de fechas. Para leer fechas, uso un solo método readDate(String textIn) que comprueba que es una fecha y que contiene el formato correcto (dd/MM/yyyy).
      public static Date readDate(String textIn) throws IOException, ParseException{
              // Declaración de variables
              BufferedReader keyboardIn=new BufferedReader(new InputStreamReader(System.in));
              SimpleDateFormat df = new SimpleDateFormat("dd/MM/yyyy");
              boolean exit=false;
              Date dateTemp=new Date();
              String date;
              // Imprimir texto de consola
              System.out.println(textIn);
      
              // Solicitar datos desde teclado
      
              do {
                  date = keyboardIn.readLine();
                  try{
                      dateTemp = df.parse(date);
      
                  } catch (Exception ex){
                  }
      
                  if (!df.format(dateTemp).equals(date)){
                      if (ConsoleTools.confirmationMessage("El formato es inválido, ¿desea introducir una nueva fecha?. " +
                              "En caso contrario, se insertará la fecha actual")) {
                      }
                      else{
                          dateTemp=new Date();
                          exit=true;
                      }
                  } else {
                      exit=true;
                  }
      
              } while (!exit);
      
              // Retornar fecha
              return dateTemp;
          }
      
  • Validación. En este grupo se incluyen métodos de validación que devuelven un valor booleano con true si se cumple la validación o false en caso contrario, como por ejemplo si el valor de una cadena es vacía o nulaboolean isNullOrEmpty(String textIn), si un número es positivo entero boolean isPositiveInteger(String numberIn), si es número entero boolean isInteger(String numberIn), si es un valor númerico doble boolean isInteger(String numberIn) o comprobando si es una fecha es válida boolean isDate(String date).
        /**
         * Comprueba si un texto es vacio o nulo
         * @param textIn Texto de entrada
         * @return True. Si es vació o nulo. False. Si contiene texto
         */
        public static boolean isNullOrEmpty(String textIn){
    
            return textIn=="" || textIn.isEmpty() || textIn==null?true:false;
    
        }
    
        /**
         * Comprueba si un número es entero y positivo
         * @param numberIn. Número de entrada
         * @return
         * True. Si es positivo y entero.
         * False. No es positivo o no es un número entero
         */
        public static boolean isPositiveInteger(String numberIn){
    
            int numberOut=0;
            // Primera validación convirtiendo el número a entero
            try {
                numberOut=Integer.parseInt(numberIn);
            } catch (Exception ex) {
                return false;
            }
            // Segunda validación comprobando que es positivo
            return numberOut&gt;0;
        }
    
        /**
         * Comprueba si un número es entero
         * @param numberIn. Número de entrada
         * @return
         * True. Si es positivo y entero.
         * False. No es positivo o no es un número entero
         */
        public static boolean isInteger(String numberIn){
    
            int numberOut=0;
            // Primera validación convirtiendo el número a entero
            try {
                numberOut=Integer.parseInt(numberIn);
            } catch (Exception ex) {
                return false;
            }
    
            return true;
        }
    
        /**
         * Comprueba si la cadena es un número
         * @param numberIn. Número de entrada
         * @return
         * True. Si es un número.
         * False. No es un número
         */
        public static boolean isNumeric(String numberIn){
    
            double numberOut=0;
            // Primera validación convirtiendo el número a double
            try {
                numberOut=Double.parseDouble(numberIn);
            } catch (Exception ex) {
                return false;
            }
    
            return true;
        }
    
        /**
         * Comprueba si la cadena es una fecha válida
         * @param date Fecha de compración
         * @return
         * True. Si es una fecha.
         * False. No es una fecha
         */
        public static boolean isDate(String date){
            try{
                SimpleDateFormat df = new SimpleDateFormat("dd/MM/yyyy");
                Date dateTemp=new Date();
                dateTemp = df.parse(date);
                return true;
                } catch (Exception ex){
                    return false;
                }
        }
    
  • Formatos y conversiones. En este grupo, se incluyen métodos que permiten mostrar los resultados de nuestras aplicaciones de un modo más legible por los que podemos disponer de un método que convierte un texto o número a un texto con formato de la moneda local String toCurrency(String number), un método que convierte a número con formato pudiendo especificar un número de dígitos String toNumber(double number) o String toNumber(double number, int digits), métodos para rellenar por la izquierda o por la derecha de un caracter String padRight(String text, int number) y String padLeft(String text, int number), un método que retorna una cadena un número de veces String dumpChar(String string, int lenght), un método que añade un borde sobre un texto String addBorder( String text, int longLine), método que convierte una fecha en formato corto String toShortDate(Date date), o convertir un tiempo en segundos en una cadena expresada en segundos, minutos, horas, días, meses y años String convertToTime(double tiempoSegundos).
    /**
         * Convierte un número a texto con formato de moneda.
         * Separador de miles, dos decimales y moneda
         * @param number Número a convertir
         * @return Cadena con formato de moneda
         */
        public static String toCurrency(String number){
            return ConsoleTools.toCurrency(Integer.parseInt(number));
        }
    
        /**
         * Convierte un número a texto con formato de moneda.
         * Separador de miles, dos decimales y moneda
         * @param number Número a convertir
         * @return Cadena con formato de moneda
         */
        public static String toCurrency(int number){
            NumberFormat fn=NumberFormat.getCurrencyInstance();
            return fn.format(number);
        }
    
        /**
         * Convierte un número a texto con formato de moneda.
         * Separador de miles, dos decimales y moneda
         * @param number Número a convertir
         * @return Cadena con formato de moneda
         */
        public static String toCurrency(double number){
            NumberFormat fn=NumberFormat.getCurrencyInstance();
            return fn.format(number);
        }
    
        /**
         * Convierte un número a texto con formato
         * @param number Número a convertir
         * @return Cadena con formato
         */
        public static String toNumber(double number){
            NumberFormat fn=NumberFormat.getNumberInstance();
            return fn.format(number);
        }
    
        /**
         * Convierte un número a texto con formato de número de decimales específico
         * @param number Número a convertir
         * @param digits Número de decimales
         * @return Cadena con formato
         */
        public static String toNumber(double number, int digits){
            NumberFormat fn=NumberFormat.getNumberInstance();
            fn.setMaximumFractionDigits(digits);
            return fn.format(number);
        }
        /**
         * Rellena por la derecha n espacios y
         * recorta el texto cuando es mayor añadiéndole
         * puntos suspensivos
         * @param text Texto formateado
         * @param number Número de espacios
         * @return Cadena de texto formateada
         */
        public static String padRight(String text, int number){
            if (text.length()&gt;number) {
                text=text.substring(0, number-3) + "...";
            }
            return String.format("%1$-" + number + "s", text);
        }
    
        /**
         * Rellena por la izquierda n espacios y
         * recorta el texto cuando es mayor añadiéndole
         * puntos suspensivos
         * @param text Texto formateado
         * @param number Número de espacios
         * @return Cadena de texto formateada
         */
        public static String padLeft(String text, int number){
            if (text.length()&gt;number) {
                text=text.substring(0, number-3) + "...";
            }
            return String.format("%1$" + number + "s", text);
        }
    
        /**
         * Retorna un caracter repetido un número de veces
         * @param string Cadena de texto
         * @param lenght Número de veces que se repite
         * @return Cadena de texto
         */
        public static String dumpChar(String string, int lenght){
            String textOut="";
    
            for (int i = 0; i &lt; lenght; i++) {
                textOut +=string;
            }
            return textOut;
        }
    
         /**
         * Devuelve un borde de texto superior e inferior
         * @param text Texto al que se le añade el borde
         * @param longLine Longitud del borde
         * @return Cadena de texto formateada con borde
         */
        public static String addBorder( String text, int longLine){
            String border = &quot;&quot;;
            border += &quot;+&quot;;
            for (int i = 0; i &lt; longLine; i++) {
                border += &quot;-&quot;;
            }
            border += &quot;+\n&quot;;
    
             return border + text + border;
        }
    
        /**
         * Convierte una fecha en formato corto
         * @param date Fecha a convertir
         * @return Cadena de texto con la fecha
         */
        public static String toShortDate(Date date){
            SimpleDateFormat df=new SimpleDateFormat(&quot;dd/MM/yyyy&quot;);
    
            return df.format(date);
        }
    
         /**
         * Convierte un tiempo en segundos en sg, min, horas, días, meses y años
         * @param tiempoSegundos Tiempo en segundos
         * @return Texto
         */
        public static String convertToTime(double tiempoSegundos){
    
            double segundos =  Math.floor(tiempoSegundos % 60);
            double aux1 =((tiempoSegundos-segundos) / 60);
            double minutos = Math.floor(aux1 % 60);
            aux1 = (aux1-minutos) / (60);
            double horas = Math.floor(aux1 % 24);
            aux1 = (aux1-horas) / 24;
            double dias = Math.floor(aux1 % 30);
            aux1 = (aux1-dias) / 30;
            double meses = Math.floor(aux1 % 12);
            aux1 = (aux1 - meses)/12;
            double años = Math.floor(aux1);
    
            String sAños = años &gt; 0? (años==1?&quot;1 año&quot;:(int)años + &quot; años &quot;):&quot;&quot;;
            String sMes = meses &gt; 0? (meses==1?&quot;1 mes&quot;:(int)meses + &quot; meses &quot;):&quot;&quot;;
            String sDias = dias &gt; 0? (dias==1?&quot;1 día&quot;:(int)dias + &quot; días &quot;):&quot;&quot;;
            String sHoras = horas &gt; 0? (horas==1?&quot;1 hora&quot;:(int)horas + &quot; horas &quot;):&quot;&quot;;
            String sMinutos = minutos &gt; 0? (minutos==1?&quot;1 minuto&quot;:(int)minutos + &quot; minutos &quot;):&quot;&quot;;
            String sSegundos = segundos &gt; 0? (segundos==1?&quot;1 segundo&quot;:(int)segundos + &quot; segundos &quot;):&quot;&quot;;
    
            return sAños + sMes + sDias + sHoras + sMinutos + sSegundos;
        }
    

Como podéis apreciar, pequeños métodos que nos permiten crear aplicaciones mucho más rápido.
En la próxima entrega, algunas utilidades de consola más como un método que crea menús automáticamente.

Un saludo

Calculadora de números complejos en Java

Calculadora de números complejos en Java
En este caso, voy a detallar una clase sencilla que implementa una calculadora de números complejos en Java.

Para los que no lo recuerden, un número complejo consta de dos partes, una parte real y una parte imaginaria; la parte imaginaria se compone del número √-1 o lo que es lo mismo i. Un número complejo lo podemos representar vectorialmente como z = (a,b), binomial z = a + bi, polar mediante un módulo y un ángulo z = r α, trigonométrica z = r (cos α + i sin α) y por último la forma exponencial y que no voy a incluir en la clase aunque a mi parecer es la más elegante e = cos α + i sin α.

Cada forma tiene su fin, por ejemplo la suma y las resta son más fáciles con la forma binomial mientras que la multiplicación, división y exponenciación lo son con la forma polar; en el caso de la clase todos los cálculos los he realizado con la forma binomial.

Si pasamos a la clase, como ya he comentado está implementada en Java y consta de cuatro propiedades, parte real mediante x, parte imaginaria mediante y, el módulo que es calculado en base de x e y, y por último el argumento o ángulo.A continuación paso el diagrama de la clase donde se exponen los métodos con la mayoría de las operaciones:

Pasando un ejemplo

        // Pasamos dos números complejos
        // Efectuamos los cálculos
        Complex c1=new Complex(4,5);
        Complex c2=new Complex(7,6);

        System.out.println(&quot;(&quot; + c1.toString() + &quot;) + (&quot; + c2.toString() + &quot;) = &quot; + c1.addComplex(c2));
        System.out.println(&quot;(&quot; +c1.toString() + &quot;) - (&quot; + c2.toString() + &quot;) = &quot; + c1.substractComplex(c2));
        System.out.println(&quot;(&quot; +c1.toString() + &quot;) x (&quot; + c2.toString() + &quot;) = &quot; + c1.multiplyComplex(c2));
        System.out.println(&quot;(&quot; +c1.toString() + &quot;) / (&quot; + c2.toString() + &quot;) = &quot; + c1.divideComplex(c2));
        System.out.println(&quot;Opuesto de &quot; + c1.toString() + &quot; = &quot; + c1.opposite().toString());
        System.out.println(&quot;Inverso  de &quot; + c1.toString() + &quot; = &quot; + c1.reverse().toString());
        System.out.println(c1.toString() + &quot; = &quot; + c1.polarToString());
        System.out.println(c1.toString() + &quot; = &quot; + c1.polarToString(true));
        System.out.println(c2.toString() + &quot; = &quot; + c2.polarToString());
        System.out.println(c2.toString() + &quot; = &quot; + c2.polarToString(true));
        c2.convertToBinomial(5, 36.86*Math.PI/180);
        System.out.println(&quot;Convertir 5) 36.86º a binomial = &quot; + c2.toString());

Resultado

(4 + 5i) + (7 + 6i) = 11 + 11i
(4 + 5i) - (7 + 6i) = -3 -1i
(4 + 5i) x (7 + 6i) = -2 + 59i
(4 + 5i) / (7 + 6i) = 0,68 + 0,13i
Opuesto de 4 + 5i = -4 -5i
Inverso  de 4 + 5i = 0,1 -0,12i
4 + 5i = 5,7)0,896 radians
4 + 5i = 5,7)51,3º
7 + 6i = 9,9)0,709 radians
7 + 6i = 9,9)40,6º
Convertir 5) 36.86º a binomial = 4 -3i

A continuación paso el código:

/******************************************************
 *  @author:    Joaquín Martínez Rus (c) 2016
 *  @version:   1.0
 *  File:       Complex.java
 *  Created:    17/07/2016
 *  Project:    Calculadora de números complejos
 *  Comments:   Clase Complex.
 *******************************************************/
public class Complex {

    /**
     * Inicia una nueva instancia de la clase Complex con valor z = 0 + 0i;
     */
    public Complex(){
        this(0,0);
    }

    /**
     * Inicia una nueva instancia de la clase Complex
     * @param _x Parte real
     * @param _y Parte imaginaria
     */
    public Complex(double _x, double _y){
        this.x=_x;
        this.y=_y;
        this.setModule();
        this.setGrades();
    }

    double x;
    double y;
    double module;
    double grades;

    public double getX() {
        return x;
    }

    public void setX(double x) {
        this.x = x;
    }

    public double getY() {
        return y;
    }

    public void setY(double y) {
        this.y = y;
    }

    public double getModule() {

        return module;
    }

    /**
     * Asigna y calcula el valor del módulo
     */
    public void setModule(){
        this.module=Math.sqrt(Math.pow(x, 2)+ Math.pow(x, 2));
    }

    /**
     * Asigna el valor del módulo
     */
    public void setModule(double module) {
        this.module = module;
    }

    public double getGrades() {
        return grades;
    }

    public void setGrades() {
        this.grades = Math.atan2(y, x);
    }

    public void setGrades(double grades) {
        this.grades = grades;
    }

    /**
     * Suma al número complejo otro número complejo
     * @param _complex Número complejo de la suma
     * @return Objeto Complex
     */
    public Complex addComplex(Complex _complex){
        return new Complex(this.x + _complex.x, this.y + _complex.y);
    }

    /**
     * Resta al número complejo otro número complejo
     * @param _complex Número complejo de la resta
     * @return Objeto Complex
     */
    public Complex substractComplex(Complex _complex){
        return new Complex(this.x - _complex.x, this.y - _complex.y);
    }

    /**
     * Multiplica al número complejo otro número complejo
     * @param _complex Número complejo de la multiplicación
     * @return Objeto Complex
     */
    public Complex multiplyComplex(Complex _complex){
        return new Complex((this.x * _complex.x - this.y * _complex.y),
                (this.x * _complex.y + this.y * _complex.x));
    }

    /**
     * Divide el número complejo actual entre el valor del parámetro
     * @param _complex Denominador
     * @return Objeto Complex
     */
    public Complex divideComplex(Complex _complex){
        return divideComplex(this,_complex);
    }

    /**
     * Divide dos números complejos
     * @param _complex1 Numerador
     * @param _complex2 Denominado
     * @return Objeto Complex
     */
    public Complex divideComplex(Complex _complex1, Complex _complex2){
        double xx = (_complex1.x * _complex2.x + _complex1.y * _complex2.y)/(Math.pow(_complex2.x,2)+Math.pow(_complex2.y,2));
        double yy = (_complex1.y * _complex2.x - _complex1.x * _complex2.y)/(Math.pow(_complex2.x,2)+Math.pow(_complex2.y,2));
        return new Complex(xx, yy);
    }

    /**
     * Obtiene un número complejo en forma vectorial
     * @return Cadena de texto
     */
    public String vectorialtoString(){
        return &quot;(&quot; + ConsoleUtilities.toNumber(this.x,2) + &quot;, &quot; + ConsoleUtilities.toNumber(this.y,2) + &quot;)&quot;;
    }

    /**
     * Obtiene un número complejo en forma binomial
     * @return Cadena de texto
     */
    @Override
    public String toString(){
        return ConsoleUtilities.toNumber(this.x,2) + (this.y &amp;lt; 0? &quot; - &quot;: &quot; + &quot;) + ConsoleUtilities.toNumber(this.y,2) + &quot;i&quot;;
    }

    /**
     * Obtiene un número complejo en forma polar
     * @return Cadena de texto
     */
    public String polarToString(){
        return  polarToString(false);
    }

    /**
     * Obtiene un número complejo en forma polar
     * @param isDegrees Mostrar como grados centigrados o radianes
     * @return Cadena de texto
     */
    public String polarToString(boolean isDegrees){
        double angle=isDegrees?this.getGrades()*180/Math.PI:this.getGrades();
        String angleToString = (isDegrees?ConsoleUtilities.toNumber(angle,1):ConsoleUtilities.toNumber(angle,3)) + (isDegrees? &quot;º&quot;: &quot; radians&quot;);
        return  ConsoleUtilities.toNumber(this.module,1) + &quot;)&quot; + angleToString;
    }

    /**
     * Obtiene el conjugado de un número complejo
     * @param complex Número complejo
     * @return Objeto Complex
     */
    public Complex conjugate(Complex complex){
        return new Complex(complex.x, - complex.y);
    }

    /**
     * Calcula el opuesto de un número complejo
     * @return Objeto Complex
     */
    public Complex opposite(){
        return opposite(this);
    }

    /**
     * Calcula el opuesto de un número complejo
     * @param _complex Número complejo
     * @return Objeto Complex
     */
    public Complex opposite(Complex _complex){
        return new Complex(-_complex.x, - _complex.y);
    }
    /**
     * Obtiene el conjugado de un número complejo
     * @return Objeto Complex
     */
    public Complex conjugate(){
        return conjugate(this);
    }

    /**
     * Obtiene el inverso de un número complejo
     * @return Objeto Complex
     */
    public Complex reverse(){
        return reverse(this);
    }

    /**
     * Obtiene el inverso de un número complejo
     * @param complex Número complejo del cálculo
     * @return Objeto Complex
     */
    public Complex reverse(Complex complex){
        double denominador=Math.pow(complex.x, 2) + Math.pow(complex.y, 2);
        return new Complex( complex.x/denominador, - complex.y/denominador);
    }

    /**
     * Convierte un número complejo de forma polar a binomial
     * @param module Modulo del número complejo
     * @param argument Argumento en radianes del número complejo
     * @return Objeto Complex
     */
    public Complex convertPolarToBinomial(double module, double argument){
        double _x = Math.abs(module) * Math.cos(argument);
        double _y = Math.abs(module) * Math.sin(argument);
        return new Complex(_x,-_y);
    } 

    /**
     * Asigna los valores real e imaginario en base al módulo y el argumento
     * @param module Modulo del número complejo
     * @param argument Argumento en radianes del número complejo
     */
    public void convertToBinomial(double module, double argument){
        Complex _complex=convertPolarToBinomial(module, argument);
        this.x=_complex.x;
        this.y=_complex.y;
    }

    /**
     * Comprueba dos números complejos
     * @param _complex Número complejo a comparar
     * @return Objeto Complex
     */
    public boolean equals(Complex _complex){
        if (_complex==null) {
            return false;
        }
        if (this.getClass()!=_complex.getClass()) {
            return false;
        }

        return this.x ==_complex.x &amp;amp;&amp;amp; this.y == _complex.y;
    }
}

Consejos.

  • La clase debe estar bien definida. Constructores, propiedades y métodos
  • Antes de escribir código, genera un diagrama de clases como mínimo (esto implica pensar que vas a hacer), además una buena estructura puede hacer nuestro código más robusto.
  • Piensa en las cuatro características de la Programación Orientada a Objetos, Abstracción, Encapsulamiento, Herencia y Polimorfismo (hay alguna más, pero estas son las principales y debes tenerlas muy claras)
  • Los métodos deben contener el código justo. Un método con mucho código no hace la clase legible y lo vuelve débil.
  • Las clases deben funcionar en cualquier medio. Si usara esta clase en modo gráfico con ventanas en vez de modo consola, debería de funcionar del igual modo, solo debo llamar al método de operación y obtener su resultado mediante los métodos apropiados.
  • Usa los comentarios, tanto dentro de los métodos como en su documentación. Por ejemplo, si llamo al método divideComplex de la clase Complex, cuando estoy escribiendo el método, aparecerá el método y el texto que nosotros escribimos, en este caso yo escribí “Divide el número complejo actual entre el valor del parámetro” junto con los parámetros y el valor retornado. Cuando las clases se hacen muy grandes y complejas, es necesario documentarlas todo lo que se pueda.
  • Además de los comentarios, a mi me gusta agrupar el código por regiones con Java uso
    // &lt;editor-fold defaultstate=&quot;collapsed&quot; desc=&quot;REQUEST FROM KEYBOARD&quot;&gt;

    /**
     * Devuelve un valor desde el teclado
     * @param textIn Texto que se visualiza en la consola
     * @return Texto procedente del teclado
     * @throws IOException
     */
    public static String readText(String textIn, boolean allowEmpty) throws IOException{

        // Declaración de variables
        BufferedReader keyboardIn=new BufferedReader(new InputStreamReader(System.in));
        String textOut=null;

        // Imprimir texto de consola
        System.out.println(textIn);

        // Solicitar datos desde teclado

        do {
            textOut=keyboardIn.readLine();
        } while (ConsoleUtilities.isNullOrEmpty(textOut) &amp;amp;&amp;amp; !allowEmpty);

        // Retornar texto
        return textOut;
    }

    // &lt;/editor-fold&gt;

Esto me va permitir expandir o contraer todo el código contenido entre las etiquetas o para el caso de C#

#region Private Methods
      // Aquí iría el código en C#
#endregion

o Visual Basic

#Región MiRegion
     // Aquí iría el código en Visual Basic
#End Region
  • Cíñete a la nomenclatura estándar con cada lenguaje de programación, por ejemplo Java o C# o Visual Basic.
  • Créate un clase con herramientas, por ejemplo, para aplicaciones del tipo consola en Java, tengo una clase con métodos estáticos donde implemento herramientas que puedo usar en este medio como un método que genera un menú automáticamente, un lector desde teclado de texto o números, un formateador de texto, un serializador, etc. Para C# tengo otro tipo de clases donde extiendo funcionalidades a las clases creadas, en fin, código que reutilizaré más a menudo de lo que me pienso.
  • Si hacemos todo esto, en un futuro nos será más fácil, modificar, entender que hicimos o ampliar nuestras clases.
Y esto es todo por hoy. Saludos!

Sistema Planetario en Java

Sistema Planetario en Java

Para practicar POO, se me ha ocurrido crear un sistema planetario, el cual contiene unos objetos que a su vez orbitan alrededor de otro objeto con mayor gravedad y así poder calcular datos entre ellos, como pueda ser la fuerza de gravitación, distancias, tiempo de llegada, etc.

Este sistema es muy precario, porque no se tienen en cuenta las órbitas, si no que se entiende que todos los objetos está alineados y la distancia que los separa, es la distancia media de esas órbitas.

Con esta aplicación, existe una superclase abstracta ObjetoSistema que contiene la mayoría de los atributos y métodos, luego hereda de esta otra clase llamada ObjetoSinLuz al cual se le pueden añadir datos de su atmósfera mediante un ArrayList y de esta clase.

Los objetos reales del sistema, Estrella, hereda de la superclase; los planetas y satélites de la clase heredan de la clase ObjetoSinLuz para poder en un momento dado añadirles datos de su atmósfera.

Cada objeto tiene como atributo un objeto tipo ObjetoSistema que es el objeto de referencia sobre el cual orbita, de modo que la distancia media del objeto será a la de su objeto de referencia.

Para calcular la distancia por ejemplo entre la Luna y Marte, se calcularía:

  • La distancia entre la Luna y la Tierra que es su objeto de referencia
  • Distancia desde la Tierra al Sol y la suma de estas dos, la distancia de la luna al sol
  • Por otra parte calcularía la distancia  de Marte al Sol y la diferencia de ambas distancias, nos daría el resultado esperado.

El cálculo de las distancias se calcula recursivamente para que funcione en cualquier nivel, por ejemplo si la Luna a su vez fuera un objeto referencia de otros satélites o asteroides.

Otra particularidad que he incluido, es la posibilidad de obtener listados por diferentes órdenes, como pueda ser el caso de ordenar por la masa, tamaño, días de traslación, etc., de modo que el método compareTo de cada clase, debe tener previsto con que atributo ordenar.

Su funcionamiento es muy sencillo mediante menú textual, (lo que se trata es aprender)

**********************************************
Sistema Solar
**********************************************
* (1) Calcular distancia entre dos objetos   *
* (2) Calcular atracción gravitacional       *
* (3) Calcular tiempo (línea recta)          *
* (4) Listar objetos                         *
* (5) Salir                                  *
**********************************************

Introduzca la opción…
3
Introduzca el nombre del primer objeto…
tierra
Introduzca el nombre del segundo objeto…
marte
Introduzca la velocidad km/h (Si pulsa INTRO, se asignará 28000 km/h…
50000
Invertirá para recorrer 78.338.770 Km a una velocidad de 50.000 Km/h, 2 meses 5 días 6 horas 46 minutos 31 segundos  días
Pulse INTRO para continuar…

Tiempo empleado por la luz en llegar desde el Sol a Júpiter.(300.000km/s=1.080.000.000 km/h)

Invertirá para recorrer 778.412.010 Km a una velocidad de 1.080.000.000 Km/h, 43 minutos 14 segundos  días

o el mismo caso para el Sol y la Tierra

Invertirá para recorrer 149.597.870 Km a una velocidad de 1.080.000.000 Km/h, 8 minutos 18 segundos  días

Como caso particular, el código para calcular la distancia, que mediante un método recursivo, hasta que no encuentra el objeto de referencia principal, va calculando la distancia entre cualquier objeto y este.

/**
 * Funcion recursiva para calcular la distancia hasta el objeto de referencia del sistema planetario
 * @param objetoSistemaInicial Objeto inicial desde donde se calcula la distancia
 * @param objetoSistema Objeto hasta donde se calculará la distancia
 * @return Distancia entre los dos objetos
 */
private double calcularDistanciaAObjetoReferencia(ObjetoSistema objetoSistemaInicial){
        double distancia = objetoSistemaInicial.distanciaMedia;
        if (!objetoSistemaInicial.objetoReferencia.objetoReferencia.equals(objetoSistemaInicial.objetoReferencia)) {
            distancia += calcularDistanciaAObjetoReferencia(objetoSistemaInicial.objetoReferencia);        }
        return distancia;
    }





Os dejo el diagrama de clases y el
código (proyecto de NetBeans) para practicar y toquetear.

Un saludo y a programar.

Pd: No está terminada, ya que le falta añadir otros elementos para añadir componentes, atmósferas, etc., pero creo que se entiende su objetivo que es el de aprender a programar.

Le he incluido el sol, los planetas y dos satélites, la Luna y Phobos, meted más!