lunes, 12 de octubre de 2015

Polimorfismo y Manejo de Excepsiones

Programación Orientada a Objetos

Polimorfismo:
La palabra polimorfismo proviene del griego y significa que posee varias formas diferentes. Este es uno de los conceptos esenciales de una programación orientada a objetos. Así como la herencia está relacionada con las clases y su jerarquía, el polimorfismo se relaciona con los métodos.
En programación orientada a objetos se denomina polimorfismo a la capacidad que tienen los objetos de una clase de responder al mismo mensaje o evento en función de los parámetros utilizados durante su invocación. Un objeto polimórfico es una entidad que puede contener valores de diferentes tipos durante la ejecución del programa.
En algunos lenguajes, el término polimorfismo es también conocido como ‘Sobrecarga de parámetros’ ya que las características de los objetos permiten aceptar distintos parámetros para un mismo método (diferentes implementaciones) generalmente con comportamientos distintos e independientes para cada una de ellas.
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.
Por ejemplo, en un lenguaje de programación que cuenta con un sistema de tipos dinámico (en los que las variables pueden contener datos de cualquier tipo u objetos de cualquier clase) como Smalltalk no se requiere que los objetos que se utilizan de modo polimórfico sean parte de una jerarquía de clases
El polimorfismo es una relajación del sistema de tipos, de tal manera que una referencia a una clase (atributo, parámetro o declaración local o elemento de un vector) acepta direcciones de objetos de dicha clase y de sus clases derivadas (hijas, nietas,…).

En general, hay tres tipos de polimorfismo:
Polimorfismo de sobrecarga
Polimorfismo paramétrico (también llamado polimorfismo de plantillas)
Polimorfismo de inclusión (también llamado redefinición o subtipado)






Polimorfismo de sobrecarga
El polimorfismo de sobrecarga ocurre cuando las funciones del mismo nombre existen, con funcionalidad similar, en clases que son completamente independientes una de otra (éstas no tienen que ser clases secundarias de la clase objeto). Por ejemplo, la clase complex, la clase image y la clase link pueden todas tener la función "display". Esto significa que no necesitamos preocuparnos sobre el tipo de objeto con el que estamos trabajando si todo lo que deseamos es verlo en la pantalla.
Por lo tanto, el polimorfismo de sobrecarga nos permite definir operadores cuyos comportamientos varían de acuerdo a los parámetros que se les aplican. Así es posible, por ejemplo, agregar el operador + y hacer que se comporte de manera distinta cuando está haciendo referencia a una operación entre dos números enteros (suma) o bien cuando se encuentra entre dos cadenas de caracteres (concatenación).
Polimorfismo paramétrico
El polimorfismo paramétrico es la capacidad para definir varias funciones utilizando el mismo nombre, pero usando parámetros diferentes (nombre y/o tipo). El polimorfismo paramétrico selecciona automáticamente el método correcto a aplicar en función del tipo de datos pasados en el parámetro.
Por lo tanto, podemos por ejemplo, definir varios métodos homónimos de addition() efectuando una suma de valores.
  • El método int addition(int,int) devolvería la suma de dos números enteros.
  • float addition(float, float) devolvería la suma de dos flotantes.
  • char addition (char, char) daría por resultado la suma de dos caracteres definidos por el autor.
  • etc.
Una signature es el nombre y tipo (estático) que se da a los argumentos de una función. Por esto, una firma de método determina qué elemento se va a llamar.
Polimorfismo de subtipado
La habilidad para redefinir un método en clases que se hereda de una clase base se llama especialización. Por lo tanto, se puede llamar un método de objeto sin tener que conocer su tipo intrínseco: esto es polimorfismo de subtipado. Permite no tomar en cuenta detalles de las clases especializadas de una familia de objetos, enmascarándolos con una interfaz común (siendo esta la clase básica).
Imagine un juego de ajedrez con los objetos reyreinaalfilcaballotorre y peón, cada uno heredan el objeto pieza
El método movimiento podría, usando polimorfismo de subtipado, hacer el movimiento correspondiente de acuerdo a la clase objeto que se llama. Esto permite al programa realizar el movimiento de pieza sin tener que verse conectado con cada tipo de pieza en particular.



Clasificación Se puede clasificar el polimorfismo en dos grandes clases:
• Polimorfismo dinámico (o polimorfismo paramétrico) es aquél en el que el código no incluye ningún tipo de especificación sobre el tipo de datos sobre el que se trabaja. Así, puede ser utilizado a todo tipo de datos compatible.
• Polimorfismo estático (o polimorfismo ad hoc) es aquél en el que los tipos a los que se aplica el polimorfismo deben ser explicitados y declarados uno por uno antes de poder ser utilizados. El polimorfismo dinámico unido a la herencia es lo que en ocasiones se conoce como programación genérica.
También se clasifica en herencia por redefinición de métodos abstractos y por método sobrecargado. El segundo hace referencia al mismo método con diferentes parámetros. Otra clasificación agrupa los polimorfismo en dos tipos: Ad-Hoc que incluye a su vez sobrecarga de operadores y coerción, Universal (inclusión o controlado por la herencia, paramétrico o genericidad).
Ejemplo:

 ‘este método devuelve la suma de 2 parámetros
 Function suma (int numero1, int numero2)
 ‘este método devuelve la suma de los 3 parámetros
 Function suma (int numero1, int numero2, int numero3)
 ‘este método devuelve la concatenación de cadena1 y cadena2
 Function suma (String cadena1, String cadena2.


Ejemplo: polimorfismo.


public class A extends B {
public void print() {
System.out.println("A");
}

}
public class B {
public void print() {
System.out.println("B");
}

}
public class C extends B {
public void print() {
System.out.println("C");
}

}
public class Main {
public static void main(String[] args) {
B[] bs = new B[3];
bs[0] = new B();
bs[1] = new A();
bs[2] = new C();
naivePrinter(bs);

}

private static void naivePrinter(B[] bs) {
for (int i = 0; i < bs.length; i++) {
bs[i].print();
}

}
}

Manejo de excepciones.
El manejo de excepciones es una técnica de programación que permite al programador controlar los errores ocasionados durante la ejecución de un programa informático. Cuando ocurre cierto tipo de error, el sistema reacciona ejecutando un fragmento de código que resuelve la situación, por ejemplo retornando un mensaje de error o devolviendo un valor por defecto.
Una excepción en términos de lenguaje de programación es la indicación de un problema que ocurre durante la ejecución de un programa. Sin embargo, la palabra excepción se refiere a que este problema ocurre con poca frecuencia generalmente cuando existe algún dato o instrucción que no se apega al funcionamiento del programa por lo que se produce un error. El manejo de excepciones permite al usuario crear aplicaciones tolerantes a fallas y robustos (resistentes a errores) para controlar estas excepciones y que pueda seguir ejecutando el programa sin verse afectado por el problema. En lenguaje java estas excepciones pueden manejarse con las clases que extienden el paquete Throwable de manera directa o indirecta, pero existen diversos tipos de excepciones y formas para manejarlas.
El manejo de excepciones ayuda al programador a trasladar el código para manejo de errores de la línea principal de ejecución, además se puede elegir entre manejar todas las excepciones, las de cierto tipo o de las de grupos relacionados, esto hace que la probabilidad de pasar por alto los errores se reduzca y a la vez hace los programas más robustos. Pero es importante utilizar un lenguaje de programación que soporte este manejo, de lo contrario el procesamiento de errores no estará incluido y hará el programa más vulnerable. Este manejo está diseñado para procesar errores que ocurren cuando se ejecuta una instrucción, algunos ejemplos son: desbordamiento aritmético, división entre cero, parámetros inválidos de método y asignación fallida en la memoria. Sin embargo, no está diseñado para procesar problemas con eventos independientes al programa como son pulsar una tecla o clic al mouse.
Las excepciones se dividen en verificadas y no verificadas. Es importante esta división porque el compilador implementa requerimientos de atrapar o declarar para las verificadas lo que hará que se detecten las excepciones automáticamente y de acuerdo al lenguaje de programación utilizado se utilizará un método para corregirlas. Sin embargo para las no verificadas se producirá un error indicando que deben atraparse y declararse. Por eso el programador debe pensar en los problemas que pueden ocurrir cuando se llama a un método y definir excepciones para verificarse cuando sean importantes. Las clases de excepciones pueden derivarse de una superclase común, por lo que con un manejador para atrapar objetos de la superclase, también se pueden atrapar todos los objetos de las subclases de esa clase. Pero también, se pueden atrapar a cada uno de los tipos de las subclases de manera individual si estas requieren ser procesadas diferente.
Limpieza de pila.
En ocasiones cuando se lanza una excepción, pero no se atrapa en un enlace específico, la pila de llamadas se limpia y el programa intenta volverlo a atrapar en el siguiente bloque, esto se conoce como limpia de pila. Este proceso hace que el método en el que no se atrapó la excepción termina, todas sus variables quedan fuera del enlace y el control regresa a la instrucción que originalmente la invocó. La limpieza de pila de repetirá hasta que la excepción pueda ser atrapada porque de lo contrario se producirá un error a la hora de compilar.
Aserciones.
Las aserciones ayudan a asegurar la validez del programa al atrapar los errores potenciales e identificar los posibles errores lógicos del desarrollo. Estas pueden escribirse como comentarios para apoyar a la persona que desarrolla el programa. Algunos ejemplos son:
Precondiciones y pos condiciones ñ Estas características son utilizadas por los programadores para hacer un análisis de lo esperado del programa antes y después de su ejecución. Son importantes porque gracias a ellas se pueden detectar posibles fallas en el programa y corregirlas.
Las precondiciones son verdaderas cuando se invoca a un método, estas describen las características del método y las expectativas que se tienen en el estado actual del programa. Si no se cumplen las precondiciones el comportamiento del método es indefinido por lo que se lanza una excepción que esté preparada o continuar con el programa esperando el error. Las pos condiciones describen las restricciones en el entorno y cualquier efecto secundario del método. Es recomendable escribirlas para saber que esperar en un futuro si es que se hacen modificaciones.
Conclusión.
El manejo de excepciones ayuda a lidiar con los errores de una aplicación por medio de la manipulación del código para hacer programas más robustos. Además existen herramientas que ayudan a manejarlas tal es el caso de los bloques try (intentar) que encierran el código que puede lanzar una excepción y los bloques el catch (atrapar) que lidian con las excepciones que surjan. También existen técnicas que el programador utiliza para conocer el posible funcionamiento del programa y detectar los errores que pueda contener.

Ejemplo:
import java.io.IOException;

// ...

public static void main(String[] args) {
try {
// Se ejecuta algo que puede producir una excepción
} catch (IOException e) {
// manejo de una excepción de entrada/salida
} catch (Exception e) {
// manejo de una excepción cualquiera
} finally {
// código a ejecutar haya o no excepción
}
}


Básicamente el manejo de excepción es un Objeto descendiente de la clase java.lang.Object, podemos pensar en ellas como una condición excepcional en nuestro sistema el cual altera la correcta ejecución del mismo, las excepciones nos indican que hay algo anómalo,  inconsistente o simplemente un Error, lo cual impide que el sistema se ejecute como debería de ser...

Tal vez se preguntaran si ¿pero Anómalo, inconsistente o Error no es básicamente lo mismo?...... podría ser, pero en este enfoque no necesariamente lo es, ya que lo que vamos a conocer como una excepción no siempre es un error (hablando como excepcion en general, ya que en java una Excepción es muy diferente a un Error), muchas veces necesitaremos trabajar con excepciones controladas para indicar alguna inconsistencia en nuestro sistema que podría provocar errores.......

A modo de ejemplo, podemos encontrarnos con el famoso NullPointerException el cual nos indica que un objeto se encuentra vació, pero esto no es un error ya que nosotros podemos trabajar con objetos null, entonces veámoslo como si la excepcion nos dijera "Es un objeto nulo y no se puede efectuar el proceso", mientras que hay errores como NoClassDefFoundError el cual nos indica que la máquina virtual de Java (JVM) no puede encontrar una clase que necesita, debido a por ejemplo que no encuentra un .class, esto si se maneja como un error en java.


Jerarquía de excepciones:


No hay comentarios:

Publicar un comentario