Algoritmos

Algoritmos:

Los algoritmos son distintas formas de usar los patrones de diseño y las estructuras de datos para conseguir hacer cosas de la forma más óptima posible.

Notación O

Es la forma de decir lo rápido que funcionará el algoritmo. Un numero mayor significa que va más lento que un numero más pequeño.

O(log n) << O(n) << O (n^log n) << O (n^n)

O (log n) se logra cuando por ejemplo, tienes que recorrerte una lista para poder hacer algo y puedes hacerlo sin tener que recorrerte toda la lista. Recorrerse la lista entera es O(n). Cuando se tiene que recorrer toda la lista y volver a visitar algunos numeros pero no la lista entera, entonces es O(n ^ log n), y si por cada elemento de la lista se tiene que volver a visitar toda la lista por ejemplo para buscar si un numero es mayor o menor, entonces se dice que es O(n^n).

Es importante intentar usar el más bajo. Ordenar una lista de por ejemplo 1.000 elementos usando un algoritmo O(n^n) puede tardar horas mientras que uno O(log n) puede tardar segundos.

Recursividad

Lo hemos visto por encima al ver las funciones de pilas y colas.

Algoritmos de Busqueda

Algoritmos de Orden

 

Pilas

Pilas

Las pilas son una estructura que almacena nodos. En ellos se lee siempre primero el ultimo nodo en llegar. Piensa en una pila de documentos del trabajo y un empleado que solo coje la hoja de más arriba.

Realmente actualmente se usan principalmente las colas, porque en las pilas cuando se coge el dato este se quita de la pila, aunque sea para verlo.

Ejemplo:

Nodos

Nodos

Un nodo es una clase que encapsula información. Sigue el siguiente patrón:


En Anterior y Siguiente se guarda otro nodo, la idea de los nodos es crear cadenas con ellos. Para los que sepáis este tema, sabreis que ese nodo solo se usa en un caso específico, que en casi todos solo se guarda el nodo anterior, no el siguiente.

En Dato aqui uso un „int“, pero realmente puede ser de cualquier tipo de clase o dato.

Los nodos se usan por lo menos en pilas, colas, listas y arboles (en arboles realmente se llaman hoja y pueden tener multiples referencias a otros nodos, no solo siguiente y anterior, ya que tienen por lo menos al nodo padre y a dos nodos hijos. Ya lo veremos en su categoría).

Matrices y arrays

Matrices y arrays

A veces tienes que crear varias variables del mismo tipo para guardar datos parecidos. Por ejemplo, imaginate que tienes la clase huevo y la clase caja de huevos. En cada clase caja de huevos puedes tener 6 huevos. Pero… ¿Y si otra vez se tienen 12 huevos? ¿Vas a tener que modificar la clase o crear una clase abstracta de la que heredan caja6huevos y caja12huevos? Hay una solución para eso: Matrices y arrays. Fijate en esta linea de código:

int [] numeros = new int[6];

Con esto hemos creado 6 variables del tipo int, a las que se accede desde el 0 hasta el 5 así:

numeros[0] = 10;

numeros[1] = 20;

numeros[5] = 22;

Otra forma de crearlos es así:

int [] numeros = {10,20,30,40,50};

Así hemos creado un array de 5 elementos. Para acceder al primero tendremos que usar el numeros[0].

 

Para crear un array de un objeto concreto usamos el siguiente comando:

Objeto [] array = new Objeto [10];
array[0] = new Objeto(); // aqui llamamos al constructor

Una matriz es un array de 2 dimensiones. Realmente pueden tener todas las dimensiones que se quieran hacer, pero realmente es algo poco común y que realmente usarás muy poco.
int [][] numeros = new int[10][10];

Así hemos creado una tabla de 10 x 10. Todo lo demás es igual que con el resto de arrays.

Así que ya hemos visto como trabajar con matrices y arrays. ¿Como hacemos lo del ejemplo del inicio? Aprovechando el constructor de la clase:

Huevo [] arrayHuevos;

void CajaHuevos(int cantidad){

arrayHuevos = new Huevo[cantidad];

}

Con esto podemos usar unas veces cajas de 12 huevos, otras de 6, y otras de lo que haga falta.

Command

Command

Problema: A la hora de llamar a funciones puede que te encuentres que quieres tratarlas como objetos para poder asignar mejor los parámetros y similares.

Solución: Con este patrón puedes encapsular una función como un objeto y poder tratarlo como tal.

Fundamento:


Básicamente, por cada función que quieras convertir en objeto, haces un comando concreto. Lo que sería la función puedes ponerla en operación y los parámetros como variables de esta clase. El resto del programa solo verá comandos por el polimorfismo y puedes cambiarlos como quieras.

Ejemplo:

Chain of Responsability

Chain of Responsability

Problema: Imaginate que un objeto llama a una función de otro objeto, y este al hacer esto llama a otro objeto, y este a otro… cada uno con distintos nombres de funciones y tal. Como hemos visto hay soluciones para esto para facilitar el trabajar con esta clase de problemas.

Solución: Vamos a usar el polimorfismo para que las clases que heredan de una puedan llamar a otras facilmente y haciendo todo más estandarizado y fácil.

Fundamento:


Un manejador tendrá referencias de otros manejadores que dependan de el. Todos los manejadores tienen la función Operación (o la que sea) y al llamarla llaman también a los objetos asociados que puedan tener que hacerlo.

Ejemplo:

Proxy

Proxy

Problema: Imaginate una interfaz, como el facade, pero que puedas cambiar para añadir flexibilidad (solo que la fachada solo se comunica con un solo objeto en vezde con varios).

Solución: Haciendo que las clases de fachada (aqui llamadas proxy) y las que hacen el trabajo hereden del mismo interfaz, hace que puedas cambiar de clase que realiza el trabajo en tiempo real y sin problemas.

Fundamento:



Ejemplo:

Flyweight

Flyweight

Problema: Se usa demasiada memoria en tu proyecto y buscas formas de optimizar

Solución: El flyweight reduce el impacto en memoria de tu programa al compartir un objeto entre varios otros para no tener que hacer cientos o miles de copias de este con información idéntica. Por ejemplo, imagina una simulación sobre un hormiguero. No es factible crear millones de copias de un objeto con la misma información.

Fundamento:


En el programa existe una factoría flyweight (que debería ser además singleton para poder llamarla desde cualquier sitio) que lo que hace realmente es almacenar todos los objetos flyweight que se han creado y lo que hace es que si el objeto existe, te lo devuelve. El objeto puede ser compartido o no, eso da igual, pero si en dos objetos distintos necesitas usar el mismo objeto, con el flyweight factory recibes el mismo objeto y no tienes que crearte dos objetos distintos con los mismos valores para hacer lo mismo.

Ejemplo:

Facade

Facade

Problema: Has hecho un montón de clases para un programa con un fin, por ejemplo, hacer gráficas a partir de unas tablas. Y decides poner el código on-line para que cualquiera lo baje (o es parte de un programa mayor) y quieres que se use bien, no que la gente se ponga a crear un objeto de una clase que no funciona bien sola.

Solución: Hacemos una interfaz que simplifique al usuario todo lo que hay detrás. El usuario no tiene que saber que para hacer una gráfica usas ciertos patrones, ciertas clases y tal… El lo que quiere es saber que clase llamar, y dentro de esa clase que funciones usar, para simplemente hacer una gráfica y olvidarse.

Vamos, lo que hemos estado haciendo hasta ahora al hacer cosas privadas o publicas.

Fundamento:


El objeto que haga de Facade sirve de interfaz para usar un sistema. Por ejemplo, para hacer una calculadora las funciones del facade serían sumar, restar, multiplicar, dividir, exponencial… Y al usuario le da igual lo que haya detras, solo sabe que llama a la función, mete dos numeros, y recibe otro.

Ejemplo:

Decorator

Decorator

Problema: Vale, tenemos familias de objetos organizados con el Composite. Pero… ¿Y si lo que quiero es dinámicamente (en mitad de la ejecución) cambiar el comportamiento de un objeto?

Solución: Así es como sale este patrón, que no es más que hacer abstracto al composite para que así se pueda sustituir cualquier miembro de la familia en tiempo de ejecución sin problemas.

Fundamento:



Existe la interfaz Componente. Cada objeto que hereda de él es o bien un componente concreto o bien un decorador. Y si es un decorador significa que puede en un momento dado ejecutar la funcion „operacion“ de un decorador concreto, mientras que 5 minutos mas tarde se ha cambiado el decorador y el programa no tiene porque saberlo, llama a la misma función pero el programa hace otra cosa.

Ejemplo:

Composite

Composite

Problema: Tienes una familia de objetos y buscas una buena forma de organizarlos.

Solución: Con este patron organizas objetos complejos a partir de unir simples de forma recursiva pero al usar la abstracción haces que todos tengan una interfaz común, lo cual es bueno para saber como usar a esa familia de objetos.

Fundamento:


Tenemos la interfaz Componente. Todos los objetos que heredan de esto son o bien composite si tienen más objetos „colgando de el“ (con la operación añadir) o hojas si no tienen hijos. Realmente esto acaba teniendo estructura de arbol (mira el siguiente capitulo sobre estructuras de datos para saber mas)

Ejemplo: Vamos a hacer una pequeña agenda telefónica de personas pero con un giro. En cada persona estarán colgadas las personas a las que conozca. Las personas serán composites y cada persona tendrá una serie de personas colgando de él (y puede haber repeticiones, haciendo que una persona enlace a quien la conoció y que puedas entrar en un bucle, el que pueda haber bucles hará que no automaticemos la busqueda, aunque si quieres intentarlo tendrás que hacer un control que detecte que lleva 3-4 veces saltando entre los mismos. Por ejemplo, prueba a añadir un booleano que diga si el objeto ha sido ya mostrado)

Bridge

Bridge

Problema: Tienes una abstracción (una generalización, una clase que hereda de otra y tal). Y quieres modificarla reduciendo al mínimo las modificaciones en el resto del código, solo en la abstracción o su implementación.

Solución: La solución es algo parecida a lo anterior. Si separamos de una clase su implementación, hacemos que en el resto del programa se use la clase sin problemas ,y a la hora de modificar se use solo la clase que sea, pero no el resto.

Fundamento:


Ejemplo:

Adaptador

Adaptador

Problema: Tienes una o varias clase que llama/usa funciones de otras clases. Si modificas estas clases usadas (nombre de función, qué devuelve…), entonces tienes que buscar y cambiar a lo largo de todo el código para dejarlo todo bien.

Solución: Crear una clase abstracta llamada „adaptador“ con una función llamada solicitud. Al resto del programa le dará igual como funcionen las clases que especifiquen esto, puesto que solo llamará a la función request. Luego esta función usará las clases que sean y pedirá los datos que sean, pero solo reduce las modificiaciones a dos clases y no a todo el programa.

Fundamento:



Ejemplo:

Singleton

Singleton

Problema: ¿Quieres hacer un objeto que sea único, que no pueda haber copia de él, y que lo puedas llamar y usar desde cualquier parte del programa? Entonces necesitas un Singleton.

Solución: El singleton es una clase que realmente no la creas, pides una instancia de él. Al hacerlo, si no existía el objeto, este se crea a sí mismo y se asegura que las proximas veces que pidas una instancia de él solo venga él. Y al ser static puedes llamar a la instancia en cualquier sitio del programa. ¿Algunos usos útiles? Para hacer informes del programa. Haces una clase que incorpore las funciones y variables del singleton, y haces que por cada fallo o cosa a registrar, se llame a esta clase para que lo registre. Así desde cualquier parte del programa se puede escribir una linea de texto en el archivo final.

Fundamento:


Al llamar a la funcion estática getInstancia(), la funcion mira si InstanciaUnica tiene algún valor o no. Si no tiene ningún valor asignado, es la primera vez que se llama la función, así que llama al constructor y guarda el nuevo singleton en instancia unica. Al final devuelve el valor que esté en instancia única.

Ejemplo:

Prototype

Prototype

Problema: Quieres un objeto, pero que no sea nuevo, si no un clon de otro ya existente. Por ejemplo, para hacerle cambios al clon pero que no afecten al original (hacer un prototipo)

Solución: El prototipo tiene como ventajas que te ahorra el tener que hacer una fábrica para este tipo de objetos, pues al ser un clon de uno ya existente el esfuerzo de programación es muy bajo, y lo mismo se traduce al rendimiento: Si tienes que crear un objeto complejo, se requiere reservar mucha memoria y eso puede ir en contra del programa, pero si se usa un clon se agiliza.

Fundamento: Tenemos el interfaz prototipo. Todas las funciones que lo implementen heredarán de él la función clonar(), además al ser una función abstracta cada hijo lo especificará comole de la gana.



Ejemplo:

Factory Method

Factory Method

Problema: Parecido al de la fábrica abstracta, tenemos que crear una familia de objetos. Pero en este caso no se esperan nuevas familias y las ya existentes estan categorizadas por cosas comunes (a diferencia de hacer una pizza, en la que los objetos masa y topping no tienen información en común).

Solución: Factory Method es otra simplificación de la fábrica abstracta. Esta se diferencia en que no se usan interfaces, si no que ya hay algunas funciones comunes definidas. Por ejemplo, un constructor para inicializar los datos comunes de las clases hijas.

Fundamento: El programa tiene un objeto de la clase fabrica abstracta, que según el producto específico a crear usará una especialización de este u otra. Recordemos que cada producto es una colección de objetos y con este patrón evitamos tener que estar creando a mano los nuevos objetos cada vez que queramos uno, reduciendo el riesgo de cometer errores.


Ejemplo:

Builder

Builder

Problema: A la hora de de hacer un nuevo objeto, la cantidad de parámetros es larguísima, indicando que hemos cometido un fallo en el diseño (un anti-patrón, que parece una solución pero no es buena). Aparte de que con muchas opciones, el diseño puede no ser lo suficientemente flexible (y la falta de flexibilidad se soluciona con clases abstractas y polimorfismo).

Solución: Este es el patrón de construcción para crear nuevos objetos más sencillo que hay en esta lista (el singleton no cuenta). Permite configurar más fácilmente la creación de estos objetos, sin tener que memorizar una lista larga de parámetros en los constructores. Es util al crear objetos a partir de XML o similares. La diferencia con las fábricas es que las fábricas crean familias de objetos, pero el builder crea objetos complejos.

Fundamento:


Tenemos una clase director, que se configura a base de cambiar las variables, y al final se le da a construir para que genere un objeto del tipo que sea. Vamos a verlo en el ejemplo.

Ejemplo:

Imaginate que tenemos una cadena de hamburguesas (antes pizzas, ahora hamburguesas) y que queremos crear una pizza según lo que nos diga el cliente. Una opción sería hacer algo como:

public Menu pedirMenu(Hamburguesa burger, Refresco ref, Postre pos, Regalo reg);

Puede que tu tengas una memoria maginifica y sepas bien como va esto pero si se lo pasas a otro programador puede tener problemas. Así que para prevenir problemas y para tener las ventajas que ofrece esto (como que si cambia el menú no se cambia el resto del programa [en principio]), vamos a hacer el siguiente diseño:

 

Abstract Factory

NOTA: Esta incompleto.

Abstract Factory

Problema: A la hora de crear objetos, resulta que tienes que crear no solo uno, si no varios, y configurarlos para que estén relacionados representando todo un grupo de clases. No es solo llamar al constructor de uno, que ese llame a otro y listo. Y se espera que en un futuro se añadan nuevas fábricas y tipos de producto. Si no entiendes bien esto ultimo mira el ejemplo, al final de él se explica lo de añadir nuevos sin modificar lo viejo.

Solución: Una clase especializada en construirte el sistema de clases listo y configurado según como se requiera. Aparte de que al tener el código de creación aparte puedes hacerlo estático para invocar la fábrica desde cualquier parte del código para crearte objetos.

Fundamento:


Es la primera vez que lees Interface. ¿Que es un interface? Es una clase sin variables en la que todas las funciones son abstractas. Lo que las hace especiales es que una clase puede heredar de solo una clase, pero puede heredar de todas las interfaces que quieras. En este caso no se llama heredar, si no implementar. Aunque es lo mismo.

Ejemplo: Vamos a realizar para el ejemplo un gestor de textos. El usuario crea un programa para gestionar pizzas. (Esta basado en el ejemplo de Headfirst. Me parece mejor ejemplo que el clásico de fabricar coches en el que los productos son puertas, carrocerías y ventanas, y en la fabrica se cogen esos)

En este caso, tenemos un gestor de pizzas que se encarga de fabricar pizzas (Debería ser más complejo e incorporar una clase Pizza para la información, pero para el ejemplo de fabrica abstracta nos vale). Las funciones escribir de los ingredientes nos servirán para saber que ingredientes tiene. La fabrica de pizzas modernas crea pizzas modernas (con salsa barbacoa y masa gruesa) mientras que la clasica crea pizzas más tradicionales de masa fina y jamón.

Ventajas de este diseño en este problema: Que si queremos añadir nuevos tipos de masa o nuevos tipos de topping… no hace falta modificar lo que ya esta hecho, es solo añadir, y así se evitan posibles fallos en el futuro: Se crea la clase ToppingHawaiana por ejemplo, que implemente Topping, y si eso se hace una nueva fabrica.


El código queda tal que así:

Interfaz Masa:

 

MasaFina:

 

MasaGruesa:

 

Interfaz Topping

 

Procciuto

 

Barbacoa

 

FabricaPizzasAbstracta

 

FabricaPizzasClasica

 

FabricaPizzasModernas

 

Gestor Pizzas

 

Ejecutando este ejemplo el resultado debería darte:

Patrones de diseño

Patrones de diseño

Ya que has aprendido lo básico de programación orientada a objetos realmente solo te falta por aprender:

  • Clases ya hechas en Java para no tener que programar algo que ya esta hecho y saber qué puedes usar en cada momento y que no. Lo reservo para un glosario al final del cursillo puesto que puedes comprobarlos en cualquier momento en el javadoc (manual de clases de Java https://docs.oracle.com/javase/8/docs/api/ . Estan catalogadas por paquetes. Un paquete es una colección de clases con un orden lógico dentro de un proyecto. Por ejemplo, yo en NetBeans dentro del proyecto „curso“ tengo un package por cada tema/proyecto del curso).
  • Patrones de diseño: Ante problemas comunes se usan soluciones típicas para cada uno. Los patrones de diseño son soluciones estándar y probadas ante cierto tipo de problemas. Una serie de clases que relacionadas de cierta forma y con ciertas funciones que hacen algo, te permiten realizar soluciones.
    • Estructuras de datos: Para muchos no son patrones de diseño pero entran en la descripción. Lo que lo diferencia respecto a los patrones de diseño es que sirven más como distintas formas de guardar los datos en vez de tratarlos.
  • Algoritmos: Te permiten resolver problemas de forma rápida y efectiva.

Vamos a ver los „originales“, los primeros en salir y más tarde veremos otros mas modernos:

Creacionales (crean objetos o dicen como son)

  • Abstract Factory
  • Builder
  • Factory Method
  • Prototype
  • Singleton

Estructurales (Resuelven problemas composición de clases y objetos)

  • Adaptador
  • Bridge
  • Composite
  • Decorator
  • Facade
  • Flyweight
  • Proxy

De comportamiento (solucionan problemas sobre cómo interactúan entre sí los objetos)

  • Chain of Responsability
  • Command
  • Interpreter
  • Iterator
  • Mediator
  • Memento
  • Observer
  • State
  • Strategy
  • Template method
  • Visitor

UML y ejemplo de uso de clases

UML

Antes de ponernos con el ejemplo voy a explicar muy rápido lo que es UML. Es cómo se diseñan los programas e indica que clases hay, como son, y como se relacionan las clases entre sí, indicando si una clase es abstracta, que clases son las hijas, si una función necesita crear un objeto de otra clase, o si en vez de una variable en una clase tenemos un objeto de otra clase … (recuerda que el objetivo de una clase es encapsular información. Si quisieramos hacer una clase DNI, esa clase tendría un objeto de la clase Persona que encapsula los datos de esa persona, luego la clase DNI incluirá la fecha de expedición y mas cosas propias del DNI, pero no de la persona porque se eso de encarga la otra clase).

Aquí puedes ver todos los ejemplos que hemos hecho hasta ahora.

A la izquierda tienes la definición normal de una clase con ejemplos de cómo poner las variables y las funciones. Solo veo aclarar que aquí el tipo de la función o de la clase se pone al final mientras que en el código va antes.

Luego en vehículo tenemos el ejemplo de polimorfismo que hemos puesto. Fijate que las funciones abstractas se escriben inclinadas. Que una clase hereda de otra se especifica con esa flecha.

Finalmente a la derecha esta el ejemplo de la persona con el DNI. Como el DNI contiene una Persona, se llama composición y se especifica con el simbolo de la linea con el rombo negro.

Para empezar esta bien con esto. Hay más cosas que saber (como cuando una clase solo necesita a otra para una sola función y no esta compuesta por ella [es igual que la composición, pero el rombo esta en blanco]).

Lo que falta lo veremos más adelante en el ejemplo.

Ejemplo:

Vamos a realizar el siguiente programita:

En este programa nos basamos en el ejemplo anterior de los vehículos. Creamos una clase principal (llamada aplicación), que tendrá objetos de la clase Persona (Los numeros sobre la linea significan que cada objeto de la clase persona pertenecen solo a 1 aplicación, y que una aplicación puede tener de 0 a muchas personas)

La clase Persona es abstracta, y las clases cartero y panadero heredan de ella (son una especialización de Persona). Las funciones que se llaman igual que la clase se llaman constructores y se usan para dar los valores iniciales a la clase.

Vamos a añadir una función mostrarDatos() que devuelve los valores de la persona como un texto (lo que hicimos en el capitulo anterior en la función main() ). Y esta vez vamos a hacer algo un poco distinto: Al calcular la edad vamos a coger la fecha actual en el ordenador. Para ello, usaremos una función que incorpora Java que nos permite recibir la fecha, y esa fecha convertirla en un numero.

Vamos a ello. Para empezar, mostramos la clase Persona:

//Mostrar clase Persona acabada

De este código solo hay dos cosas que quizás no entiendas:

1_ Las funciones abstractas tienen que llevar el texto abstract al inicio. Y al haber una función abstracta la clase es abstracta: No se pueden crear objetos de esta clase directamente, si no objetos hijos.

2_ Al coger el año del orenador usamos la clase „Calendar“ de Java. Para ello se pone en la parte de arriba del todo „import Java.util.Calendar“. Luego se usa de la forma que se muestra: Se coge una instancia de la clase (Eso es porque es un Singelton. No te preocupes, es un patrón de diseño que veremos en el siguiente capítulo). Cuando tenemos la instancia, llamamos a la función get, que nos devolverá un valor. Le decimos que el valor que queremos es el año.

El código del calendario se puede mostrar así para hacerlo más fácil de leer:

Calendar cal = Calendar.getInstance();

int anyo = cal.get(Calendar.YEAR);

Ahora veamos las clases hijas:

// Codigo clase Panadero

//Codigo clase Cartero

Como estas clases heredan de Persona, se pone al principio que „extienden“ esta clase. Para específicar la función trabajar hay que poner antes el „@Override“ para indicar al programa que es la misma función que hay en Persona, pero que la estas modificando. Esto se hace también si quieres modificar en la clase actual alguna función de la clase padre.

Cuando en estas clases se usa „super“ se llama a la clase padre. En este caso se usa en los constructores para pasarle los datos iniciales, y para llamar a la función coger datos. Una persona te da los datos, que luego los usa el carpintero o el panadero para decirte su profesión.

Finalmente vamos a escribir la clase principal. Creamos una función que le dice a la persona que trabaje (para que nos diga que oficio tiene y sus datos), y luego en la función main creamos un panadero y un cartero. Como queremos cambiar de uno a otro sin problemas para usar los beneficios del polimorfismo, usamos la clase Persona para crearlos.

En total, tiene que quedar así:

En println estamos usando un texto raro. El „\n“… ¿Que significa? Que añadimos manualmente un salto de linea mas. Con esto hacemos el resultado más fácil de leer. Así. El texto del final con este ejemplo debe ser:
run:

Creamos la primera persona y la mostramos

Nombre: Pepe. Telefono: 1. Año de nacimiento: 1986. Edad: 29

Soy carpintero y corto madera

Creamos la segunda persona

Nombre: Jorge. Telefono: 2. Año de nacimiento: 1991. Edad: 24

Soy panadero y hago pan

BUILD SUCCESSFUL (total time: 0 seconds)

Clases, herencia y polimorfismo

Clases, herencia y polimorfismo

Ahora que ya sabes las bases de programación, vamos a ver un paso más avanzado. Primero vamos a aclarar los conceptos de clase, herencia y polimorfismo. Luego, veremos UML para explicar el ejemplo que vamos a hacer (UML es una forma de representar mediante gráficos un conjunto de clases y su relacción entre ellas), y finalmente haremos el ejemplo.

Clases:

Como dijimos en el capitulo anterior, una clase es una colección de variables y funciones que indica como serán los objetos que se hagan de ella. Sería un molde de repostería del que sacamos los distintos objetos-pasteles. El ejemplo sería que tenemos la clase persona, que se compone de las variables fecha de nacimiento, telefono y nombre, y la función de calcular la edad. Y de esa clase podremos hacer varios objetos. Por ejemplo:

  1. Nombre= PericoFecha nacimiento = 1991

    Telefono = 11111111

  2. Nombre= FulanoFecha nacimiento = 1977

    Telefono = 2222222

  3. Nombre= FulanoFecha nacimiento = 1977

    Telefono = 2222222

¿Los dos objetos del final son iguales? No, tienen el mismo valor, pero están alojados en distintos puntos de la memoria y se tratan como dos objetos diferentes. Es como si tienes una variable numerica llamada X con el valor 5 y otra variable numérica llamada Y con el valor 5. No son la misma variable, pero tienen el mismo valor.

El objetivo de las clases es agrupar variables y funciones en grupos lógicos (como la citada persona) pero también encapsular esa información. Por ejemplo, imagina que tenemos otra clase llamada DNI. Esa clase puede tener las mismas variables y funciones que una persona. Entonces… ¿Para que hacer la clase persona? Pues porque así en vez de tener en la clase DNI las variables de la persona y las variables del dni (como material de fabricación, fecha de expedición, tamaño…), puede tener un objeto de la clase persona y luego el resto de variables propias del DNI, y si la clase persona tiene datos que son privados, la clase DNI no podrá acceder a ellos. Los datos de la persona estan encapsulados en su clase. Y el código final es más fácil de leer al reducir la función de calcular la edad, nombre, telefono y fecha de nacimiento a solo crear un objeto y llamar a las partes necesarias. Esto último lo verás en el ejemplo al final.

Herencia:

Una vez que tienes una clase lista y terminada, y tienes que crear una nueva clase que comparte muchas cosas en común, es posible crear una clase con las cosas que tienen ambas clases en común y hacer que estas dos clases hereden de la clase „padre“ (la que tiene las cosas en común) y en las clases „hijas“ se pondrán las cosas específicas de cada una.

Un ejemplo típico es decir que tenemos la clase “Coche“, con su depósito, puertas, tamaño, etc… ¿Que pasa si queremos hacer la clase „Motocicleta“? Para empezar no tiene puertas…

La solución es crear una clase padre que podríamos llamar „vehiculo“, que tiene las variables comunes como el depósito, el tamaño, etc. De esa clase heredarán las clases „Coche“ (que añade la variable numero de puertas y maletero) y la clase „Motocicleta“ que añade cilindrada y tipo de carnet.

Polimorfismo:

El polimorfismo es un paso más allá de la herencia. Quiere decir que si creamos un objeto del tipo vehiculo, podemos asignarle un coche o una moto. Por desgracia, el ejemplo pasado no nos sirve sin mas, hace falta hacer un par de cambios:

  • La clase padre tiene que ser abstracta. Esto quiere decir que por lo menos una función no tiene nada de código dentro. Luego lo vemos en el ejemplo.
  • Las clases hijas son las que se encargarán de darle el código.

Para que pueda funcionar, vamos a añadir una función a la clase vehiculo llamada „avanzar“. Esta función será abstracta. No tiene código dentro y se declararía asi:

public abstract void avanzar();

Recuerda del capítulo anterior: Esta función sería publica, se puede llamar. No es estática como la del capítulo anterior, por lo que hace falta crear un objeto de una clase hija (las clases abstractas no pueden tener objetos) para acceder a la función. Indicamos que es abstracta con „abstract“, la función se llama „avanzar“, y como no tenemos código ponemos el punto y coma al final.

Luego las clases hijas tendrían por ejemplo en la moto:

@override

public void avanzar(){

System.out.println(„giro el puño de aceleración“);

}

Mientas en el coche tendría:

@override

public void avanzar(){

System.out.println(„piso el pedal de aceleración“);

}

El @override indica al programa que estas reescribiendo una función de la clase padre. Esto se puede hacer también en herencia.

Finalmente, cuando vaya a usar uno u otro, pondré en la función main algo como:

vehiculo veh = new moto();

veh.avanzar();

veh = new coche();

veh.avanzar();

Vaya, aquí es la primera vez que hemos creado un objeto y no te he hablado todavía de los constructores de objeto. Eso lo explico en el ejemplo más adelante. Con esto te digo que el resultado es:

giro el puño de aceleración

piso el pedal de aceleración

Has creado un solo objeto, que has hecho que sea una moto, y luego un coche, y usando la misma función tiene resultados distintos. Esto es bueno para mantener el código, dar una jerarquía mejor, y dar un patrón de comportamiento a las clases hijas (porque si solo hubiera herencia, al ser funciones que hacen distintas cosas no las veríamos como algo común para la clase padre, y podrían llamarse de forma distinta y no se podría automatizar el avanzar independientemente de si es un coche o una moto)

Tu primer programa

Hoy vamos a ver el tema que considero más complejo y largo porque es el que más contenido mete de una sola vez. El resto es más sencillo, y si se te ha quedado algo de aqui a medias, como más adelante se sigue usando, espero que se aclare. Del mismo modo, si más adelante tienes problemas para entender algo, vuelve aqui.

Primeros pasos:

Antes de empezar a programar, abre tu editor de preferencia (repito, para mi el Netbeans, tu el que quieras) y crea un proyecto (suele haber un botón para ello. Esto te creará una carpeta donde se guardará el código que escribas para usarlo más tarde).

En Netbeans, elije en categorías “Java” y dentro “Java Application”. Dale al botón de siguiente y ponle al proyecto el nombre que quieras, como “Proba”, “Prueba”,”CursoProgramacion01″, o lo que prefieras.

Ya estas listo para programar.

Se te ha creado una pantilla automática mostrando la clase principal y la función principal. Te explico un poco el tema:

  • Java es un lenguaje de programación orientado a objetos. Lo que creas son clases (cada clase contiene la información sobre una cosa real) en donde se ponen funciones y variables, u objetos a partir de una clase (por ejemplo, tenemos la clase “Persona”, luego en otra clase como “Familia” puedes tener dos objetos de la clase Persona llamados por ejemplo Pepe y Pepa respectivamente). Si esto es lioso ahora no te preocupes, mas adelante lo vemos mas a fondo y con ejemplos lo entiendes bien.
  • La función Main es la función principal. Cuando empieza un programa, empieza aquí.

Eso es lo que estas leyendo ahora mismo. Voy a hacer una pausa para explicar un poco las palabras que hemos visto y no entiendes para aclararlas un poco antes de seguir:

  • Variable: Es una unidad de información. Puede ser por ejemplo un numero, una palabra, una frase, etc. Para cada unidad de información se usará una variable distinta. Por ejemplo, si volvemos a usar la clase persona, podemos darle tres variables: Año de nacimiento (de tipo numero), nombre (de tipo texto), y numero de telefono (de tipo numero)
  • Sentencia: cada una de las distintas ordenes que le das al ordenador en un programa. Crear una variable es una sentencia. Darle un valor es otra sentencia. etc. Mas adelante veremos mas tipos de sentencias. todas ellas acaban en “;” para indicar que se acaban, porque pueden tener varias lineas de longitud.
  • Funciones: Al programar hay varias sentencias iguales que se utilizan una vez, y otra y otra. Para no estar cogiendo todo el rato las mismas 5 sentencias, copiarlas y pegarlas, creamos funciones. Con esto haces el código más sencillo de leer. Piensa en una función como una máquina en la que metes unos valores/variables y a cambio te da una variable distina. Luego las vemos más a fondo.

Escribimos código

Comenzamos con el programa. Escribe (ESCRIBE, no copies y pegues. Si no, no aprendes) esto dentro de la función Main

De tal forma que quede así:

Si le das al simbolo del play te ejecuta el programa y te muestra:

Nombre: Pepe. Año nacimiento: 86. Telefono: 666666666
BUILD SUCCESSFUL (total time: 0 seconds)

Analizemos lo que hemos puesto

Como hemos dicho, al iniciar el programa se llama a la función “main”. Así que empieza por la primera sentencia:


Sentencia 1

String nombre = "Pepe";

Esta sentencia crea una variable de tipo texto (String) llamada nombre, a la que se le asigna el valor “Pepe” (cuando se mete texto, tiene que ir entre comillas “” )

Prueba a cambiar el texto entre las comillas (“). Prueba a poner “Federica Bernarda Lombarda” en lugar de “Pepe”, y vuelve a ejecutar el programa (darle al boton play) y te dará este resultado:

Nombre: Federica Bernarda Lombarda. Año nacimiento: 86. Telefono: 666666666


Sentencia 2

int anyoNacimiento = 86;

Esta sentencia crea una variable de tipo numérico (int de Integer que es un numero entero. Si quieres usar un numero decimal hay que usar otros tipos como “float” o “double”) llamada anyoNacimiento (fijate que esta junto y con ny en lugar de ñ. Esto es porque los nombres de las variables son convertidas a idioma del ordenador al compilar, y si hay espacio o carácteres raros como acentos, ñ, ß o similares, el ordenador no lo entiende bien y te da fallos). Fijate que va sin comillas, al contrario que los textos.

Prueba a poner por ejemplo 1976 en lugar de 86


Sentencia 3

int telefono = 666666666;

Igual que la anterior


Sentencia 4

System.out.println(“Nombre: ” + nombre + “. Año nacimiento: ” +
anyoNacimiento + “. Telefono: ” + telefono );

Esta sentencia es especial: Tiene 2 lineas de longitud (y podría tener más). Pero la sentencia acaba cuando se llega al punto y coma.

La parte de System.out es que llama a un objeto llamado out dentro de la colección de clases y objetos “System”. Esta clase out se encarga de mostrar cosas. Este objeto tiene varias funciones. Entre ellas print (imprime en el texto de abajo todo el texto que pongas entre paréntesis), o la que usamos aqui: Println. La diferencia es que cada vez que uses println vas a crear una linea de texto con su salto de linea al final, pero si usas print “a secas” se va a poner el texto nuevo seguido del anterior en vez de en una nueva linea.

Entre parentesis esta lo que se pasa a la función: Un texto (String). Los textos tienen de bueno que se pueden añadir unos a otros. Por eso se “suman”. Al poner <"Nombre" + nombre> le decimos al programa que escriba el texto Nombre y le añada el contenido de la variable “nombre”, que ahora mismo es “Pepe”

Mejoramos el programa: Añadimos funciones

Pongamos que queremos saber la edad que tiene la persona en un determinado año.

Para saberlo, vamos a crear una función. Recuerda: Piensa en una función como una máquina en la que metes unos valores/variables y a cambio te da una variable distina

Cuando se hace una función, hay que definir dos cosas:

  • Que variables recibe
  • Que variable da como resultado.

Luego en su interior se ponen las sentencias que se repiten a menudo o que queremos apartar de una función más grande para hacerla más fácil de leer.

Vamos a escribir esto:

 

Te comento la primera linea:

  • Al principio se tiene que poner si quieres que la función sea publica o privada (“public” o “private”). Esto significa si cuando se crea un objeto de esta clase se puede ejecutar este trozo de código o no. Algunas funciones es necesario dejarlas privadas si el código va a ser usado luego por mas personas o para asegurarte de que no cometes fallos.
  • Static se tiene que usar en esta clase. En la clase en la que está la función main todas las funciones que use esa función tienen que ser static, o no las podrás usar en main.
  • “int” indica que es lo que devuelve. En este caso es un entero (int). Pero si quisiera devolver un texto pondría String.
  • HallarEdad: Es el nombre de la función. Cuando quiera ejecutar este código llamare a esta función con su nombre y le pondré lo que viene a continuación:
  • (int nacimiento, int anyoActual) : Entre parentesis indico que valores le meto a la función. Estos se llaman parámetros. Le meto dos numeros: Uno que se llama nacimiento (que será la fecha de nacimiento de la persona) y anyoActual que es el año en el que estamos ahora. Recordemos que no se puede usar la ñ ni poner espacios en los nombres, por lo que ponemos ny y juntamos las palabras. Para leer facilmente la segunda ponemos la primera letra en mayusculas.

Luego en el contenido de la función (entre los corchetes) te lo explico rápido:

  • Creamos una variable de tipo numero, llamada edad, y la ponemos de valor 0.
  • Esto es nuevo: Le asignamos el resultado de restar a anyoActual el valor de nacimiento. Esto no modifica a anyoActual, ni a nacimiento. Solo a Edad, que recibe el valor calculado al otro lado. Puedes añadir al final de la linea un “*2” (sin comillas) para multiplicar el resultado por 2. Esto no modifica nada a las otras dos variables, solo a Edad.
  • Con la palabra “return” decimos que tiene que devolver la función. En este caso decimos que tiene que devolver el valor de Edad (recuerda, devuelve valor, no la variable)

Esto esta muy bien, pero… No esta optimizado. Es otra cosa importante. Simplificarlo hace que el programa vaya más rápido y ocupe menos memoria. Para programas tan pequeños no suele ser necesario pero en programas mas grandes la diferencia se nota. Al final del artículo te comento como optimizarlo.

Vamos a añadir la función. Escribe esto en la función main:

Lo que estamos haciendo al final es que escriba en la linea el valor que devuelve la función. A la función la pasamos el valor que contiene la variable año de nacimiento (1986) y le pasamos el año actual (2015). Recuerda, estamos pasando el valor de la variable, no la variable. Esto no modifica a la variable.

Al final, la función devuelve un numero (29) y la pone en el texto de la frase. del mismo modo que se comentó antes.

Prueba a ejecutar y verás como funciona. Ahora la frase te calcula también la edad.

Otra forma de usar la función sería crear una nueva variable y asignarle el resultado de la función. Por ejemplo

String Edad = hallarEdad(anyoNacimiento, 2020);

Optimizando la función

 

En la linea 1 se asigna un valor y en la 2 se asigna otro valor. ¿Por que no mejor hacer solo una asignación?

int Edad = anyoActual - nacimiento;

En la ultima linea se devuelve el valor de Edad, y en la anterior se le da un valor a Edad. ¿Y si directamente devuelve el valor que se asignaría a Edad?

 

Así queda simplificado en una sola linea. Evitamos al ordenador que reserve memoria para una variable, y le ahorramos 3 calculos (asignación, resta, asignación)

Aquí estamos haciendolo una sola vez. Pongamos que en vez de tardar 0.1 segundos en hacerse todo con esto ahora tardaría 0.07. Entonces si estuviesemos trabajando con un programa que tuviese que hacer esto miles o millones de veces, en vez de tardar por ejemplo 10 minutos podría acabar en 7. Pillas la idea, ¿Verdad?

Conclusión:

Ahora ya sabes algunos datos comunes, como funcionan los programas, funciones, etc.

Realmente te separa ya muy poco para poder hacer otros programas sencillos: Saber el resto de tipos de datos que hay, otras clases de la API de Java que te puedan facilitar cosas como las clases de matemáticas, etc. Muchas de estas cosas las puedes encontrar en otras páginas de internet si tienes ganas y aun no he publicado la siguiente entrada.

Prueba a hacer más funciones: Haz una función que devuelva la edad que tendrá la persona en un numero de años (puede recibir en un parámetro el resultado de la anterior función automáticamente si llamas a la función de calcular la edad al llamar esto, en otro parámetro se puede meter en cuantos años y podría quedar algo así): “edadEnAnyos(hallarEdad(anyoNacimiento, 2015), 25) ” para conseguir la edad que tendrá en 15 años una persona que en el 2015 naciese en el año de nacimiento indicado.

Tomate esta entrada con tiempo. En la siguiente veremos las clases, herencia, polimorfismo, y el resto de tipos de datos.

Introducción a la programación

Palabras previas:

En este curso aprenderás lo básico de programación. Esta pensado para que de 0 comprendas los fundamentos y puedas realizar tus propios programas.

Este curso va a estar centrado sobre todo en Java. ¿Porque este y no otro?

A grandes rasgos:

  • C y C++ requieren de librerías externas para todo lo que no sea en consola de comandos y que se tenga en cuenta la memoria
  • C# funciona solo en windows. Si lo usas olvidate de mac o linux. C# es en un 99,9% java pero en .Net (tecnología de Microsoft). Si no tienes problema con eso adelante, pero a mi me gusta más usar tecnologías de empresas que no han sido denunciadas por monopolio.
  • Visual Basic tiene el mismo problema de .Net que C#
  • Xcode/objetive C: Solo sirve si quieres hacer algo para iOS (iPhone, iPad). O para Mac si no te sirven ni C, C++ o Java.
  • Pyton/ Ruby/ Lua/ Ada/ Perl/ etc. No son estandar generales de la industria. Estan bien para hacer ciertas aplicaciones, saber usarlos no está de más, son fáciles de aprender y tienen ventajas de Java en ellas. Pero normalmente en las empresas suelen pedir Java antes que estas.

 

  • Java: En java “puro” tu programa correrá igual en Linux, Windows o Mac. Aparte es el lenguaje para programar en Android. Si necesitas una librería (normalmente) no necesitas buscarla y descargarla de internet porque esta incluida en el pack.

Y dentro de java puedes usar el editor que quieras (en programación los llamamos IDE): Bloc de notas, Eclipse, o lo que sea. Yo uso Netbeans. Es el “Oficial” y su aspecto me parece más estable que Eclipse, que es el otro gran entorno de desarrollo. Idealmente deberías usar ambos al menos una vez y ver como hacer de todo en los dos. Pero luego usarás más el que más te guste.

Puedes bajarte Java y Netbeans aqui:
http://www.oracle.com/technetwork/es/java/javase/downloads/index.html

Si quieres otro buscalo en Google.

Dar ordenes a máquinas

Existen definiciones más formales sobre el tema, pero simplificando para todos los públicos:
-El objetivo de hacer un programa, es dar una serie de ordenes a la máquina/ordenador que se esté usando para que haga algo.

 

Ya está. Eso es todo. Si te fijas en el código fuente de un programa verás que todo es Instrucción tras instrucción:

  • Haz esto
  • Comprueba lo otro

Esa es la mayor simplificación posible. El ordenador hace estas ordenes tan rápido como le es posible, y gracias a eso funciona la informática.

Dejame ponerte un ejemplo: En un videojuego tu crees que estas viendo como un personaje hace algo, pero realmente la máquina lo que hace es comprobar que teclas estás pulsando, coloca al jugador en posición, actualiza el resto del entorno, y dibuja todo lo que ve la cámara (para los expertos: Si, lo se, estoy simplificando mucho la cosa pero de nuevo, a grandes rasgos,esto es lo que hay que hacer) y hace esto entre 30 y 60 veces por segundo.

 

Y con eso esta la introducción lista. En la siguiente entrada veremos como hacerte tu primer programa, como va el tema, las famosas variables y funciones, y todo eso.

hasta entonces