viernes, 20 de noviembre de 2015

PILAS Y COLAS


Los desarrolladores utilizan los arrays y las variantes de listas enlazadas para construir una gran variedad de estructuras de datos complejas. Las Pilas, las Colas . 

 Pilas 

La Pila es una estrucutra de datos donde las inserciones y recuperaciones/borrados de datos se hacen en uno de los finales, que es conocido como el top de la pila. Como el último elemento insertado es el primero en recuperarse/borrarse, los desarrolladores se refieren a estas pilas como pilas LIFO (last-in, first-out).
Los datos se push (insertan) dentro y se pop (recuperan/borran) de la parte superior de la pila. La siguiente figura ilustra una pila con tres String cada uno insertado en la parte superior de la pila:
Como muestra la figura anterior, las pilas se construyen en memoria. Por cada dato insertado, el itém superior anterior y todos los datos inferiores se mueven hacia abajo. Cuando llega el momento de sacar un ítem de la pila, se recpupera y se borra de la pila el ítem superior (que en la figura anterior se revela como "third").
Las pilas son muy útiles en varios escenarios de programación. Dos de los más comunes son:
  • Pilas que contienen direcciones de retorno: 
    Cuando el código llama a un método, la dirección de la primera instrucción que sigue a la llamada se inserta en la parte superior de la pila de llamadas de métodos del thread actual. Cuando el método llamado ejecuta la instrucción return, se saca la dirección de la parte superior de la pila y la ejecución continúa en sa dirección. Si un método llama a otro método, el comportamiento LIFO de la pila asegura que la instrucción return del segundo método transfiere la ejecución al primer método, y la del primer método transfiere la ejecución al código que sigue al código que llamó al primer método. Como resultado una pila "recuerda" las direcciones de retorno de los métodos llamados.
  • Pilas que contienen todos los parámetros del método llamado y las variables locales: 
    Cuando se llama a un método, la JVM reserva memoria cerca de la dirección de retorno y almacena todos los parámetros del método llamado y las variables locales de ese método. Si el método es un método de ejemplar, uno de los parámetros que almacena en la pila es la referencia this del objeto actual.
Es muy común implementar una pila utilizando un array uni-dimensional o una lista de enlace simple. En el escenario del array uni-dimensional, una variable entera, típicamente llamada top, contiene el índice de la parte superior de la pila. De forma similar, una variable de referencia, también nombrada noramlmente como top, referencia el nodo superior del escenario de la lista de enlace simple.
He modelado mis implementaciones de pilas después de encontrar la arquitectura del API Collections de Java. Mis implementaciones constan de un interface Stack para una máxima flexibilidad, las clases de implementación ArrayStack yLinkedListStack, y una clase de soporte FullStackException. Para facilitar su distribución, he empaquetado estas clases en un paquete llamado com.javajeff.cds, donde cds viene de estructura de datos complejas. El siguiente listado presenta el interfaceStack:
// Stack.java 

package com.javajeff.cds; 

public interface Stack  { 
     boolean isEmpty (); 
     Object peek (); 
     void push (Object o); 
     Object pop (); 
}
Sus cuatro métodos determinan si la pila está vacía, recuperan el elemento superior sin borrarlo de la pia, situan un elemento en la parte superior de la pila y el último recuera/borra el elemento superior. Aparte de un constructor específico de la implementación, su programa únicamente necesita llamar a estos métodos.
El siguiente listado presenta una implementación de un Stack basado en un array uni-dimensional:
// ArrayStack.java 

package com.javajeff.cds; 

public class ArrayStack implements Stack  { 
     private int top = -1; 
     private Object [] stack; 

     public ArrayStack (int maxElements) { 
            stack = new Object [maxElements]; 
     } 

     public boolean isEmpty () { 
            return top == -1; 
     } 
 
     public Object peek () { 
            if (top < 0) 
                    throw new java.util.EmptyStackException (); 
            return stack [top]; 
     } 

     public void push (Object o) { 
            if (top == stack.length - 1) 
                    throw new FullStackException (); 
            stack [++top] = o; 
     } 

     public Object pop () { 
            if (top < 0) 
                    throw new java.util.EmptyStackException (); 
            return stack [top--]; 
     } 
} 
ArrayStack revela una pila como una combinación de un índice entero privado top y variables de referencia de un array uni-dimensional stacktop identifica el elemento superior de la pila y lo inicializa a -1 para indica que la pila está vacía. Cuando se crea un objeto ArrayStack llama a public ArrayStack(int maxElements) con un valor entero que representa el número máximo de elementos. Cualquier intento de sacar un elemento de una pila vacía mediante pop() resulta en el lanzamiento de unajava.util.EmptyStackException. De forma similar, cualquier intento de poner más elementos de maxElements dentro de la pila utilizando push(Object o) lanzará una FullStackException, cuyo código aparece en el siguiente listado:
// FullStackException.java 

package com.javajeff.cds; 

public class FullStackException extends RuntimeException { 
} 
Por simetría con EmptyStackExceptionFullStackException extiende RuntimeException. Como resultado no se necesita añadirFullStackException a la clausula throws del método.
El siguiente listado presenta una implementación de Stack utilizando una lista de enlace simple:
// LinkedListStack.java 

package com.javajeff.cds; 

public class LinkedListStack implements Stack  { 
     private static class Node { 
            Object o; 
            Node next; 
     } 

     private Node top = null;  

     public boolean isEmpty () { 
            return top == null; 
     } 

     public Object peek () { 
            if (top == null) 
                    throw new java.util.EmptyStackException (); 
            return top.o; 
     } 

     public void push (Object o) { 
            Node temp = new Node (); 
            temp.o = o; 
            temp.next = top; 
            top = temp; 
     } 

     public Object pop () { 
            if (top == null) 
                    throw new java.util.EmptyStackException (); 
            Object o = top.o; 
            top = top.next; 
            return o; 
     } 
} 
LinkedListStack revela una pila como una combinación de una clase anidada privada de alto nivel llamada Node y una variable de referencia privada top que se inicialia a null para indicar una pila vacía. Al contrario que su contrapartida del array uni-dimensional,LinkedListStack no necesita un constructor ya que se expande dinámicamente cuando se ponen los ítems en la pila. Así, void push(Object o) no necesita lanzar una FullStackException. Sin embargo, Object pop() si debe chequear si la pila está vacía, lo que podría resultar en el lanzamiento de una EmptyStackException.
Ahora que ya hemos visto el interface y las tres clases que generan mis implementaciones de las pilas, juguemos un poco. El siguiente listado muestra casi todo el soporte de pilas de mi paquete com.javajeff.cds:
// StackDemo.java 

import com.javajeff.cds.*; 

class StackDemo { 
     public static void main (String [] args)  { 
            System.out.println ("ArrayStack Demo"); 
            System.out.println ("---------------"); 
            stackDemo (new ArrayStack (5)); 
            System.out.println ("LinkedListStack Demo"); 
            System.out.println ("--------------------"); 
            stackDemo (new LinkedListStack ()); 
     } 

     static void stackDemo (Stack s) { 
            System.out.println ("Pushing \"Hello\""); 
            s.push ("Hello"); 

            System.out.println ("Pushing \"World\""); 
            s.push ("World"); 

            System.out.println ("Pushing StackDemo object"); 
            s.push (new StackDemo ()); 

            System.out.println ("Pushing Character object"); 
            s.push (new Character ('C')); 

            System.out.println ("Pushing Thread object"); 
            s.push (new Thread ("A")); 

            try { 
                    System.out.println ("Pushing \"One last item\""); 
                    s.push ("One last item"); 
            } 
            catch (FullStackException e)  { 
                    System.out.println ("One push too many"); 
            } 

            System.out.println (); 
            
            while (!s.isEmpty ()) 
                 System.out.println (s.pop ()); 
            try { 
                    s.pop (); 
            } 
            catch (java.util.EmptyStackException e) { 
                    System.out.println ("One pop too many"); 
            } 
            System.out.println (); 
     } 
} 
Cuando se ejecuta StackDemo, produce la siguiente salida:
ArrayStack Demo 
--------------- 
Pushing "Hello" 
Pushing "World" 
Pushing StackDemo object 
Pushing Character object 
Pushing Thread object 
Pushing "One last item" 
One push too many 

Thread[A,5,main] 
C 
StackDemo@7182c1 
World 
Hello 
One pop too many 

LinkedListStack Demo 
-------------------- 
Pushing "Hello" 
Pushing "World" 
Pushing StackDemo object 
Pushing Character object 
Pushing Thread object 
Pushing "One last item" 

One last item 
Thread[A,5,main] 
C 
StackDemo@cac268 
World 
Hello 
One pop too many 

Colas

La Cola es una estructura de datos donde la inserción de ítem se hace en un final (el fin de la cola) y la recuperación/borrado de elementos se hace en el otro final (el inicio de la cola). Como el primer elemento insertado es el primero en ser recuperado, los desarrolladores se refieren a estas colas como estructuras FIFO (first-in, first-out).
Normalmente los desarrolladores tabajan con dos tipos de colas: lineal y circular. En ambas colas, la inserción de datos se realiza en el fin de la cola, se mueven hacia adelante y se recuperan/borran del incio de la cola. La siguiente figura ilustra las colas lineal y circular:
La cola lineal de la figura anterior almacena cuatro enteros, con el entero 1 en primer lugar. Esa cola está llena y no puede almacenar más datos adicionales porque rear identifica la parte final de la cola. La razón de la posición vacía, que identifica front, implica el comportamiento lineal de la cola. Inicialmente, front y rear identifican la posición más a la izquierda, lo que indica que la cola está vacía. Para almacenar el entero 1, rear avanza una posición hacia la derecha y almacena 1 en esa posición. Para recuperar/borrar el entero 1, front avanza una posición hacia la derecha.
Nota:
Para señalar que la cola lineal está vacía, no necesita gastar una posición, aunque esta aproximación algunas veces es muy conneniente. En su lugar asigne el mismo valor que indique una posición no existente a front y a rear. Por ejemplo, asumiendo una implementación basada en un array uni-dimensional, front y rear podrían contener -1. El índice 0 indica entonces la posición más a la izquierda, y los datos se insertarán empezando en este índice.
Cuando rear identifique la posición más a la derecha, la cola lineal podría no estar llena porque front podría haber avanzado almenos una posición para recuperar/borrar un dato. En este esceario, considere mover todos los ítems de datos hacia la izquierda y ajuste la posición de front y rear de la forma apropiada para crear más espacio. Sin embargo, demasiado movimiento de datos puede afectar al rendimiento, por eso debe pensar cuidadosamente en los costes de rendimiento si necesita crear más espacio.
La cola circular de la figura anterior tiene siete datos enteros, con el entero 1 primero. Esta cola está llena y no puede almacenar más datos hasta que front avance una posición en sentido horario (para recuperar el entero 1) y rear avance una posición en la misma direción (para identificar la posición que contendrá el nuevo entero). Al igual que con la cola lineal, la razon de la posición vacía, que identifica front, implica el comportamiento circular de la cola. Inicialmente, front y rear identifican la misma posición, lo que indica una cola vacía. Entonces rear avanza una posición por cada nueva inserción. De forma similar, front avanza una posición por cada recuperación/borrado.
Las colas son muy útiles en varios escenarios de programación, entre los que se encuentran:
  • Temporización de Threads: 
    Una JVM o un sistema operativo subyacente podrían establecer varias colas para coincidir con diferentes prioridades de los threads. La información del thread se bloquea porque todos los threads con una prioridad dada se almacenan en una cola asociada.
  • Trabajos de impresión: 
    Como una impresora normalmente es más lenta que un ordenador, un sistema operativo maneja los trabajos de impresión en un subsistema de impresión, que inserta esos trabajos de impresión en una cola. El primer trabajo en esa cola se imprime primero, y así sucesivamente.
Los desarrolladores normalmente utilizan una array uni-dimensional para implementar una cola. Sin embargo, si tienen que co-existir múltiple colas o las inserciones en las colas deben ocurrir en posiciones distintas a la última por motivos de prioridades, los desarrolladores suelen cambiar a la lista doblemente enlazada. Con un array uni-dimensional dos variables enteras (normalmente llamadas front y rear) contienen los índices del primer y último elemento de la cola, respectivamente. Mis implementaciones de colas lineales y circulares usan un array uni-dimensional y empiezan con el interface Queue que puede ver en el siguiente listado:
// Queue.java 

package com.javajeff.cds; 

public interface Queue { 
     void insert (Object o); 
     boolean isEmpty (); 
     boolean isFull (); 
     Object remove (); 
} 
Queue declara cuatro métodos para almacenar un datos, determinar si la cola está vacía, determinar si la cola está llena y recuperar/borrar un dato de la cola. Llame a estos métodos (y a un constructor) para trabajar con cualquier implementación deQueue.
El siguiente listado presenta una a implementación de Queue de una cola lineal basada en un array uni-dimensional:
// ArrayLinearQueue.java 

package com.javajeff.cds; 

public class ArrayLinearQueue implements Queue { 
     private int front = -1, rear = -1; 
     private Object [] queue; 

     public ArrayLinearQueue (int maxElements) { 
            queue = new Object [maxElements]; 
     } 

     public void insert (Object o) { 
            if (rear == queue.length - 1) 
                    throw new FullQueueException (); 
            queue [++rear] = o; 
     } 

     public boolean isEmpty () { 
            return front == rear; 
     } 

     public boolean isFull () { 
            return rear == queue.length - 1; 
     } 

     public Object remove () { 
            if (front == rear) 
                    throw new EmptyQueueException (); 
            return queue [++front]; 
     } 
} 
ArrayLinearQueue revela que una cola es una combinación de variables privadas frontrear, y queuefront y rear se inicializan a -1 para indicar una cola vacía. Igual que el constructor de ArrayStack llama a public ArrayLinearQueue(int maxElements)con un valor entero que especifique el número máximo de elementos durante la construcción de un objeto ArrayLinearQueue.
El método insert(Object o) de ArrayLinearQueue lanza una FullQueueException cuando rear identifica el elemento final del array uni-dimensional. El código de FullQueueException aparece en el siguiente listado:
// FullQueueException.java 

package com.javajeff.cds; 

public class FullQueueException extends RuntimeException { 
} 
El método remove() de ArrayLinearQueue lanza una EmptyQueueException cuando los objetos front y rear son iguales. El siguiente listado presenta el código de esta clase:
// EmptyQueueException.java 

package com.javajeff.cds; 

public class EmptyQueueException extends RuntimeException { 
} 
El siguiente listado presenta una implementación de Queue para una cola circular basada en un array uni-dimensional:
// ArrayCircularQueue.java 

package com.javajeff.cds; 

public class ArrayCircularQueue implements Queue { 
     private int front = 0, rear = 0; 
     private Object [] queue; 

     public ArrayCircularQueue (int maxElements) { 
            queue = new Object [maxElements]; 
     } 

     public void insert (Object o) { 
            int temp = rear; 
            rear = (rear + 1) % queue.length; 
            if (front == rear) { 
                    rear = temp; 
                    throw new FullQueueException (); 
            } 
            queue [rear] = o; 
     } 

     public boolean isEmpty () { 
            return front == rear; 
     } 

     public boolean isFull () { 
            return ((rear + 1) % queue.length) == front; 
     } 

     public Object remove () { 
            if (front == rear) 
                    throw new EmptyQueueException (); 
            front = (front + 1) % queue.length; 
            return queue [front]; 
     } 
} 
ArrayCircularQueue revela una implementación, en terminos de variables privadas y un constructor, muy similar aArrayLinearQueue. El método insert(Object o) es interesante porque guarda el valor actual de rear antes de hacer que esa variable apunte a la siguiente posición. Si la cola circular está llena, rear restaura su valor original antes de lanzar unaFullQueueException. La restauración de rear es necesaria porque front es igual a rear (en ese punto), y una subsecuente llamada a remove() resulta en la lanzamiento de una EmptyQueueException (incluso aunque la cola circular no esté vacía).

Después de estudiar el código del interface y de varias clases que lo implementan basándose en arrays uni-dimensionales

miércoles, 7 de octubre de 2015

SERVLETS


El servlet es una clase en el lenguaje de programación Java, utilizada para ampliar las capacidades de un servidor. Aunque los servlets pueden responder a cualquier tipo de solicitudes, éstos son utilizados comúnmente para extender las aplicaciones alojadas por servidores web, de tal manera que pueden ser vistos como applets de Java que se ejecutan en servidores en vez de navegadores web. Este tipo de servlets son la contraparte Java de otras tecnologías de contenido dinámico Web, como PHP y ASP.NET.
La palabra servlet deriva de otra anterior, applet, que se refiere a pequeños programas que se ejecutan en el contexto de un navegador web.
El uso más común de los servlets es generar páginas web de forma dinámica a partir de los parámetros de la petición que envíe el navegador web.


APPLETS

Un applet Java es un applet escrito en el lenguaje de programación Java. Los applets de Java pueden ejecutarse en unnavegador web utilizando la Java Virtual Machine (JVM), o en el AppletViewer de Sun.
Entre sus características podemos mencionar un esquema de seguridad que permite que los applets que se ejecutan en el equipo no tengan acceso a partes sensibles (por ej. no pueden escribir archivos), a menos que uno mismo le dé los permisos necesarios en el sistema; la desventaja de este enfoque es que la entrega de permisos es engorrosa para el usuario común, lo cual juega en contra de uno de los objetivos de los Java applets: proporcionar una forma fácil de ejecutar aplicaciones desde el navegador web.
En Java, un applet es un programa que puede incrustarse en un documento HTML, es decir en una página web. Cuando un navegador carga una página web que contiene un applet, este se descarga en el navegador web y comienza a ejecutarse. Esto permite crear programas que cualquier usuario puede ejecutar con tan solo cargar la página web en su navegador.

POLIMORFISMO

En programación orientada a objetos, el polimorfismo se refiere a la propiedad por la que es posible enviar mensajes sintácticamente iguales a objetos de tipos distintos. El único requisito que deben cumplir los objetos que se utilizan de manera polimórfica es saber responder al mensaje que se les envía. La apariencia del código puede ser muy diferente dependiendo del lenguaje que se utilice, más allá de las obvias diferencias sintácticas.

El Polimorfismo es uno de los 4 pilares de la programación orientada a objetos (POO) junto con la Abstracción, Encapsulación y Herencia. Para entender que es el polimorfismo es muy importante que tengáis bastante claro el concepto de la Herencia, Para empezar con esta entrada, se ha de decir que el término “Polimorfismo” es una palabra de origen griego que significa “muchasformas”. Este termino se utiliza en la POO para “referirse a la propiedad por la que es posible enviar mensajes sintácticamente iguales a objetos de tipos distintos“.

Como esta definición quizás sea algo difícil de entender, veremos un ejemplo. En este ejemplo vamos a tener una clase padre (SelecciónFutbol) en la que tendremos los atributos y métodos comunes a todos los integrantes que forman la selección de fútbol (Futbolistas, Entrenadores, Masajistas, etc.) y en ella se van a implementar los métodos del comportamiento “genérico” que deben de tener todos los integrantes de la selección. Como ya todos sabemos acerca de la herencia, la herencia no es más que sacar “factor común” del código que escribimos, así que los atributos y métodos de la clase
SeleccionFutbol los tendrán también los objetos de las clases Futbolista, Entrenador y Masajista. Antes de seguir vamos a mostrar el código de la clase “SeleccionFutbol” para ver algunas peculiaridades:

 // clase padre SeleccionFutbol
public abstract class SeleccionFutbol {
protected int id;
protected String nombre;
protected String apellidos;
protected int edad;
// constructores, getter y setter
public void viajar() {
System.out.println("Viajar (Clase Padre)");
}
public void concentrarse() {
System.out.println("Concentrarse (Clase Padre)");
}
// IMPORTANTE -> METODO ABSTRACTO => no se implementa en la clase abstracta pero si en la clases hijas
public abstract void entrenamiento();
public void partidoFutbol() {
System.out.println("Asiste al Partido de Fútbol (Clase Padre)");
}
}
Lo primero que nos debe de llamar la atención al ver este código es que utilizamos dos veces la palabra reservada “abstract“. Esta palabra nos indica que la clase “SeleccionFutbol” es una clase abstracta y las clases abstractas no se pueden instanciar, por tanto nunca podremos hacer un “new SeleccionFutbol()”. Otra cosa que vemos es que también utilizamos la palabra reservada abstract en un método (en el método entrenamiento). Esto quiere decir que todas las clases hijas de la clase “SeleccionFubol” tienen que tener implementado ese método obligatoriamente. Por tanto con esto que se acaba de contar y diciendo que la palabra “Polimorfismo” significa “muchas formas”, podéis deducir que la clase “SeleccionFutbol” es una clase que puede adoptar diferentes formas y en este ejemplo puede adoptar las formas de“Futbolista”, “Entrenador” y “Masajista”. Como vemos un “Entrenador”, un “Futbolista” y un “Masajista” pertenecen a la misma clase padre y por eso se instancian diciendo que es una SeleccionFutbol y son nuevos objetos de las clases hijas. Por otro lado vemos que no se pueden crear objetos de una clase abstracta, por tanto el crearnos el objeto “casillas” nos da un error.

Y ahora si hemos dicho que hemos definido en la clase padre un método abstracto que es obligatorio implementar en las clases hijas ¿Cómo lo hacemos? Bueno vamos por partes. Una cosa muy buena que tiene la herencia y el polimorfismo, es que las clases hijas no solo heredan los métodos (o la implementación de los métodos) de las clases padre, sino que las clases hijas se pueden especializar. Esto significa que una clase hija puede “redefinir” los métodos de su clase padre; es decir, que se puede volver a escribir ese método y de ahí la especialización. Para ello vamos a ver la
implementación de las clases hijas:

// clase hija Futbolista
public class Futbolista extends SeleccionFutbol {
private int dorsal;
private String demarcacion;
// constructor, getter y setter
@Override
public void entrenamiento() {
System.out.println("Realiza un entrenamiento (Clase Futbolista)");
}
@Override
public void partidoFutbol() {
System.out.println("Juega un Partido (Clase Futbolista)");
}
public void entrevista() {
System.out.println("Da una Entrevista");
}
}
// clase hija Entrenador
public class Entrenador extends SeleccionFutbol {
private int idFederacion;
// constructor, getter y setter
@Override
public void entrenamiento() {
System.out.println("Dirige un entrenamiento (Clase Entrenador)");
}
@Override
public void partidoFutbol() {
System.out.println("Dirige un Partido (Clase Entrenador)");
}
public void planificarEntrenamiento() {
System.out.println("Planificar un Entrenamiento");
}
}
// clase hija Masajista
public class Masajista extends SeleccionFutbol {
private String titulacion;
private int aniosExperiencia;
// constructor, getter y setter
@Override
public void entrenamiento() {
System.out.println("Da asistencia en el entrenamiento (Clase Masajista)");
}
public void darMasaje() {
System.out.println("Da un Masaje");
}
}

Como vemos en el código todas las clases hijas tienen implementada el método “entrenamiento()” ya que como dijimos al tenerlo en la clase padre como método abstracto, es obligatorio que todas las clases hijas tengan ese método. Por otro lado observamos en el código que encima del método “entrenamiento()” y otros métodos, tenemos la etiqueta “@Override“. Esta etiqueta sirve para indicar en el código que estamos “re-escribiendo o especializando” un método que se encuentra en la clase padre y que queremos redefinir en la clase hija. Si nos fijamos esta etiqueta solo y exclusivamente está en los métodos de las clases hijas que tenemos definida en la clase padre, por tanto cuando se llame a esos métodos, las clases hijas ejecutaran el método redefinido en la clase hija y las que no lo hayan redefinido se ejecutará es método de la clase padre. En la siguiente imagen vemos cómo hacemos estas especializaciones:
Con todo esto ya podemos empezar a ejecutar el programa que simulará el comportamiento de los integrantes de la selección española de futbol y ver las diferentes formas que adoptan cada uno de los integrantes de la selección. Para ello empecemos mostrando el siguiente fragmento de código:

public class Main {
// ArrayList de objetos SeleccionFutbol. Idenpendientemente de la clase hija a la que //pertenezca el objeto
public static ArrayList<SeleccionFutbol> integrantes = new ArrayList<SeleccionFutbol>();
public static void main(String[] args) {
SeleccionFutbol delBosque = new Entrenador(1, "Vicente", "Del Bosque", 60, 28489);
SeleccionFutbol iniesta = new Futbolista(2, "Andres", "Iniesta", 29, 6, "Interior Derecho");
SeleccionFutbol raulMartinez = new Masajista(3, "Raúl", "Martinez", 41, "Licenciado en Fisioterapia", 18);
integrantes.add(delBosque);
integrantes.add(iniesta);
integrantes.add(raulMartinez);
// CONCENTRACION
System.out.println("Todos los integrantes comienzan una concentracion. (Todos ejecutan el mismo método)");
for (SeleccionFutbol integrante : integrantes) {
System.out.print(integrante.getNombre() + " " + integrante.getApellidos() + " -> ");
integrante.concentrarse();
}
// VIAJE
System.out.println("nTodos los integrantes viajan para jugar un partido. (Todos ejecutan el mismo método)");
for (SeleccionFutbol integrante : integrantes) {
System.out.print(integrante.getNombre() + " " + integrante.getApellidos() + " -> ");
integrante.viajar();
}
}

Como vemos nos hemos creado tres objetos de la clase SeleccionFutbol que adoptan una de las tres formas que pueden adaptar (Entrenador, Futbolista y Masajista) y los metemos en un “ArrayList” de objetos de la clase “SeleccionFutbol”. Ahora al ejecutar este fragmento de código vamos a ver que todos tienen el mismo comportamiento a la hora de “concentrarse()” y “viajar()”, por tanto ejecutarán el método de la clase padre:
Todos los integrantes comienzan una concentracion. (Todos ejecutan el mismo método)

Vicente Del Bosque -> Concentrarse (Clase Padre)
Andres Iniesta -> Concentrarse (Clase Padre)
Raúl Martinez -> Concentrarse (Clase Padre)
Todos los integrantes viajan para jugar un partido. (Todos ejecutan el mismo método)
Vicente Del Bosque -> Viajar (Clase Padre)
Andres Iniesta -> Viajar (Clase Padre)
Raúl Martinez -> Viajar (Clase Padre)

Hasta el momento nada nuevo y sorprendente, pero ahora vamos a ver como cada uno de los integrante al lanzarse los mismos métodos (“entrenamiento()” y “partidoFutbol()”) tienen un comportamiento diferente:
........
// ENTRENAMIENTO
System.out.println("\nEntrenamiento: Todos los integrantes tienen su función en un entrenamiento (Especialización)");
for (SeleccionFutbol integrante : integrantes) {
System.out.print(integrante.getNombre() + " " + integrante.getApellidos() + " -> ");
integrante.entrenamiento();
}
// PARTIDO DE FUTBOL
System.out.println("nPartido de Fútbol: Todos los integrantes tienen su función en un partido (Especialización)");
for (SeleccionFutbol integrante : integrantes) {
System.out.print(integrante.getNombre() + " " + integrante.getApellidos() + " -> ");
integrante.partidoFutbol();
}
........
Vemos el resultado al ejecutar este fragmento de código:
Entrenamiento: Todos los integrantes tienen su función en un entrenamiento (Especialización)
Vicente Del Bosque -> Dirige un entrenamiento (Clase Entrenador)
Andres Iniesta -> Realiza un entrenamiento (Clase Futbolista)
Raúl Martinez -> Da asistencia en el entrenamiento (Clase Masajista)
Partido de Fútbol: Todos los integrantes tienen su función en un partido (Especialización)
Vicente Del Bosque -> Dirige un Partido (Clase Entrenador)
Andres Iniesta -> Juega un Partido (Clase Futbolista)
Raúl Martinez -> Asiste al Partido de Fútbol (Clase Padre)

En este caso vemos que todos los integrantes ejecutan el método “entrenamiento()” de forma diferente ya que al ser este método abstracto en la clase padre, les forzamos a las clases hijas a que implementen ese método. Por el contrario al ejecutar el método “partidoFutbol()” vemos que el objeto de la clase Masajista utiliza el método implementado en la clase padre y en cambio los objetos de la clase Futbolista y Entrenador ejecutan sus método “re-implementados o especializados” que se volvieron a escribir en sus clases.
Por último vamos a ver que cada uno de los objetos puede ejecutar métodos propios que solamente ellos los tienen como son el caso de “planificarEntrenamiento(), entrevista() y darMasaje()” que solo los pueden ejecutar objetos de la clase Entrenador, Futbolista y Masajista respectivamente:

// PLANIFICAR ENTRENAMIENTO
System.out.println("nPlanificar Entrenamiento: Solo el entrenador tiene el método para planificar un entrenamiento:");
System.out.print(delBosque.getNombre() + " " + delBosque.getApellidos() + " -> ");
((Entrenador) delBosque).planificarEntrenamiento();
// ENTREVISTA
System.out.println("nEntrevista: Solo el futbolista tiene el método para dar una entrevista:");
System.out.print(iniesta.getNombre() + " " + iniesta.getApellidos() + " -> ");
((Futbolista) iniesta).entrevista();
// MASAJE
System.out.println("nMasaje: Solo el masajista tiene el método para dar un masaje:");
System.out.print(raulMartinez.getNombre() + " " + raulMartinez.getApellidos() + " -> ");
((Masajista) raulMartinez).darMasaje();
........
Como resultado de la ejecución de este fragmento de código tenemos lo siguiente:
Planificar Entrenamiento: Solo el entrenador tiene el método para planificar un entrenamiento:
Vicente Del Bosque -> Planificar un Entrenamiento
Entrevista: Solo el futbolista tiene el método para dar una entrevista:
Andres Iniesta -> Da una Entrevista
Masaje: Solo el masajista tiene el método para dar un masaje:
Raúl Martinez -> Da un Masaje

CONCLUSION: Como hemos visto el polimorfismo es un concepto un poco más avanzado que la herencia y puede ser muy útil a la hora de jerarquizar y querer dar un patrón de comportamiento común a una serie de objetos que heredan de la misma clase.

INTERFACES

Una interfaz en Java es una colección de métodos abstractos y propiedades.
En las interfaces se especifica qué se debe hacer pero no su implementación. Serán las clases que implementen estas interfaces las que describan la lógica del comportamiento de los métodos.
La principal diferencia entre interface y abstract es que un interface proporciona un mecanismo de encapsulación de los protocolos de los métodos sin forzar al usuario a utilizar la herencia.
Una interfaz define un tipo (al igual que una clase define un tipo) pero ese tipo no provee de ningún método podemos preguntarnos: ¿qué se gana con las interfaces en Java? La implementación (herencia) de una interfaz no podemos decir que evite la duplicidad de código o que favorezca la reutilización de código puesto que realmente no proveen código.  


En cambio sí podemos decir que reúne las otras dos ventajas de la herencia: favorecer el mantenimiento y la extensión de las aplicaciones. ¿Por qué? Porque  al definir interfaces permitimos la existencia de variables polimórficas y la invocación polimórfica de métodos.

Un aspecto fundamental de las interfaces en Java es hacer lo que ya hemos dicho que hace una interfaz de forma genérica: separar la especificación de una clase (qué hace) de la implementación (cómo lo hace). Esto se ha comprobado que da lugar a programas más robustos y con menos errores. Pensemos en el API de Java. Por ejemplo, disponemos de la interfaz List que es implementada por las clases ArrayList y LinkedList (y también por otras varias clases).
interface java


El hecho de declarar una variable de tipo lista, por ejemplo List <String> miLista; nos dice que miLista va a ser una implementación de List, pero todavía no hemos definido cuál de las posibles implementaciones va a ser. De hecho, el código podría definir que se implementara de una u otra manera en función de las circunstancias usando condicionales. O a nivel de programación, mantendríamos la definición como List y nos permitiría comprobar el rendimiento de distintas configuraciones (hacer funcionar miLista bien como ArrayList bien como LinkedList viendo su rendimiento). La variable declarada se crea cuando escribimos miLista = new LinkedList <String> (); o también se puede usar la sintaxis: List <String> miLista = new LinkedList <String> ();

EJEMPLO SENCILLO DE INTERFACE EN JAVA
Vamos a ver un ejemplo simple de definición y uso de interface en Java. Las clases que vamos a usar y sus relaciones se muestran en el esquema. Escribe el código y ejecútalo.
interface java

CLASE ABSTRACTA

Este Tipo de Clases nos permiten crear “método generales”, que recrean un comportamiento común, pero sin especificar cómo lo hacen, veamos lo de esta manera.

Hay ocasiones, cuando se desarrolla una jerarquía de clases en que algún comportamiento está presente en todas ellas pero se materializa de forma distinta para cada una. Por ejemplo, pensemos en una estructura de clases para manipular figuras geométricas. Podríamos pensar en tener una clase genérica, que podría llamarse FiguraGeometrica y una serie de clases que extienden a la anterior que podrían ser Circulo, Poligono, etc. Podría haber un método dibujar dado que sobre todas las figuras puede llevarse a cabo esta acción, pero las operaciones concretas para llevarla a cabo dependen del tipo de figura en concreto (de su clase). Por otra parte la acción dibujar no tiene sentido para la clase genérica FiguraGeometrica, porque esta clase representa una abstracción del conjunto de figuras posibles.
Para resolver esta problemática Java proporciona las clases y métodos abstractos. Un método abstracto es un método declarado en una clase para el cual esa clase no proporciona la implementación (el código). Una clase abstracta es una clase que tiene al menos un método abstracto. Una clase que extiende a una clase abstracta debe implementar los métodos abstractos (escribir el código) o bien volverlos a declarar como abstractos, con lo que ella misma se convierte también en clase abstracta.

Declaración e implementación de métodos abstractos

Siguiendo con el ejemplo del apartado anterior, se puede escribir:
abstract class FiguraGeometrica {
    . . .
    abstract void dibujar();
    . . .
}

class Circulo extends FiguraGeometrica {
    . . .
    void dibujar() {
        // codigo para dibujar Circulo
        . . .
    }
La clase abstracta se declara simplemente con el modificador abstract en su declaración. Los métodos abstractos se declaran también con el mismo modificador, declarando el método pero sin implementarlo (sin el bloque de código encerrado entre {}). La clase derivada se declara e implementa de forma normal, como cualquier otra. Sin embargo si no declara e implementa los métodos abstractos de la clase base (en el ejemplo el método dibujar) el compilador genera un error indicando que no se han implementado todos los métodos abstractos y que, o bien, se implementan, o bien se declara la clase abstracta.

Referencias y objetos abstractos

Se pueden crear referencias a clases abstractas como cualquier otra. No hay ningún problema en poner:
FiguraGeometrica figura;
Sin embargo una clase abstracta no se puede instanciar, es decir, no se pueden crear objetos de una clase abstracta. El compilador producirá un error si se intenta:
FiguraGeometrica figura = new FiguraGeometrica();
Esto es coherente dado que una clase abstracta no tiene completa su implementación y encaja bien con la idea de que algo abstracto no puede materializarse.
Sin embargo utilizando el up-casting  si se puede escribir:
FiguraGeometrica figura = new Circulo(. . .);
figura.dibujar();
Cabe resaltar que al hacerlo utilizando el up-casting estas haciendo referencia al polimorfismo de una manera involuntaria, pero esto lo entederemos mejor cuando veamos polimorfismo.

La invocación al método dibujarse resolverá en tiempo de ejecución y la JVM llamará al método de la clase adecuada. En nuestro ejemplo se llamará al método dibujarde la clase Circulo.

viernes, 21 de agosto de 2015

HERENCIA

La herencia es un mecanismo exigido a cualquier lenguaje que pretenda ser orientado a objetos. Consideraremos la herencia como la transmisión de los métodos y atributos de una clase a otra. Gracias a la herencia se pueden establecer jerarquías entre clases. Establecer una jerarquía es un proceso subjetivo, que depende del programador y de los matices de apreciación de cada uno.
La herencia nos permite definir una jerarquía en la que existirán clases padre y clases hijo, pudiéndo ser una clase padre de otra clase, e hijo de otra clase a la vez. No deben definirse jerarquías de herencia en la que una clase sea padre e hijo de la misma clase. Existen 2 tipos de herencia: La herencia simple y la herencia múltiple. En la herencia simple una clase sólo puede tener una clase padre, en la herencia múltiple, una clase puede tener más de una clase padre. Por ejemplo:
En la siguiente figura se muestra una jerarquía de clases, con herencia simple de 2 clases vertebrado e invertebrado, que heredan de la clase serVivo.
Jerarquía de clases: Herencia simple
En la siguiente figura se muestra una jerarquía de clases, con herencia múltiple de 1 clase hispano-argentino, que hereda de las clases español y argentino.
Jerarquía de clases: Herencia múltiple
Cuando una clase hereda de otra, la clase padre "transmite" todos sus atributos y métodos a la clase hija.

Clase abstracta
Al construir una jerarquía de herencia puede darse el caso de que ciertas operaciones de la clase padre no pueden ser completadas por diferentes motivos, o que dichas operaciones se hagan de una forma diferente en cada clase hija. Por ejemplo, en una clase serVivo existirá un método comer, pero ese método será diferente en una clase hija perro (los perros comen con la boca), y en una clase hija pájaro, ya que los pájaros comen con el pico (salvo una mutación extraña...). Por lo tanto, una clase será abstracta si tiene algún método diferido, es decir, declarado pero no definido. No podrá instanciarse ningún objeto de una clase abstracta.
Redefinición de métodos
Una clase hija de una clase abstracta puede redefinir los métodos diferidos de su clase padre abstracta.

Nota: Una clase hija de una clase abstracta no tiene porqué redefinir los métodos diferidos de la clase padre, podría ser una clase nieta de la clase abstracta la que hiciera la redefinición. Lo que es obligatorio es redefinir un método diferido en alguna clase descendiente de una clase abstracta.
El método debe redefinirse con los mismos parámetros que el método diferido, de lo contrario se estaría definiendo otro método (sobrecarga). Es aquí donde aparece el concepto de super. Esta palabra reservada hace referencia a un método perteneciente a la clase padre de la clase del objeto en cuestión.