miércoles, 9 de abril de 2008

Polimorfismo

  • Sobrecarga
  • Sobre-escritura
  • Ligadura dinámica


La herencia es mucho más potente que simplemente una herramienta de reutilización


Cuando estamos aprendiendo programación orientada a objetos, nos maravilla la potencia de la herencia y la reutilización de código, en este paper vamos a ver que otras ventajas de la programación orientada a objetos se basan en la herencia.

El polimorfismo, como su mismo nombre sugiere múltiples formas, se refiere a la posibilidad de acceder a un variado rango de funciones distintas a través del mismo interfaz. O sea, que, en la práctica, un mismo identificador puede tener distintas formas (distintos cuerpos de función, distintos comportamientos) dependiendo, en general, del contexto en el que se halle inserto. El polimorfismo se puede establecer mediante la sobrecarga, sobre-escritura y la ligadura dinámica.

Sobrecarga.
El término sobrecarga se refiere al uso del mismo identificador u operador en distintos contextos y con distintos significados.


Si para cada funcionalidad necesitada fuese necesario escribir un método, el código resultante sería inmanejable, imagínense, por ejemplo que los desarrolladores de java hubiesen creado un método para escribir en pantalla una cadena de texto, otro diferente para escribir un entero, otro para escribir un doble, otro para escribir un carácter, otro para una cadena y un entero y así para todas las combinaciones posibles, seria casi imposible conocer dichos métodos en totalidad; en cambio sabemos que con “System.out.print()” o “System.out.println()” podemos escribir cualquier mensaje en pantalla.

Este tipo de codificación nos es permitido gracias a la sobrecarga, la cual se aplica a métodos y constructores.

La sobrecarga de métodos conduce a que un mismo nombre pueda representar distintos métodos con distinto tipo y número de parámetros, manejados dentro de la misma clase. En el ámbito de la POO, la sobrecarga de métodos se refiere a la posibilidad de tener dos o más métodos con el mismo nombre pero funcionalidad diferente. Es decir, dos o más métodos con el mismo nombre realizan acciones diferentes. El compilados usará una u otra dependiendo de los parámetros usados. Esto también se aplica a los constructores. De hecho, es la aplicación más habitual de la sobrecarga.

La forma de diferenciar varios métodos sobrecargados es a través de sus parámetros, ya sea por la cantidad, el tipo o el orden de los mismos, veamos un ejemplo:


public class Articulo {
private float precio;
public void setPrecio() {
precio = 3.50;
}
public void setPrecio(float nuevoPrecio) {
precio = nuevoPrecio;
}
public void setPrecio(float costo, int porcentajeGanancia) {
precio = costo + (costo * porcentajeGanancia);
}
}


Sobre-escritura:
La sobre-escritura, se aplica a los métodos y esta directamente relacionada a la herencia y se refiere a la re-definición de los métodos de la clase base en las subclases, por ejemplo, en la relación de herencia del ejemplo de las figuras aunque la clase base “Figura” tiene los métodos “calcularArea” y “calcularPerimetro”, las subclases “Circulo”, “Cuadrado”, “Triangulo” y “Rectangulo” redefinen estos métodos ya que el calculo del área y el perímetro de cada uno de ellos es diferente.


class Figura {
protected double area;
protected double perimetro;
public Figura() {
this.area=0;
this.perimetro=0;
}
public double getArea() {
return area;
}
public double getPerimetro() {
return perimetro;
}
public void calcularArea(){}
public void calcularPerimetro(){}
}

public class Circulo extends Figura {
private double radio;
public Circulo() {
super();
}
public double getRadio() {
return radio;
}
public void setRadio(double radio) {
this.radio = radio;
}
public void calcularArea() {
this.area = Math.PI*Math.pow(this.radio,2.0);
}
public void calcularPerimetro() {
this.perimetro = 2*Math.PI*this.radio;
}
}



Ligadura dinámica:
Gracias a la ligadura dinámica, pueden invocarse operaciones en objetos obviando el tipo actual del éstos hasta el momento de la ejecución del código, es decir que me perite definir elementos como un tipo e instanciarlos como un tipo heredado. Pero cual puede ser la utilidad de este concepto, para que me sirve obviar el tipo de un objeto para luego tomar esta decisión?.

Gracias a que en java la definición de los tipos de objetos se puede producir por atado posterior (late binding), no nos debe preocupar a que tipo de elemento le paso un mensaje ya que el compilador tomara la decisión de que objeto ejecutará que método de acuerdo a la forma de crear la instancia.

Este concepto es bastante complejo de entender ya que estamos acostumbrados a definir los elementos de acuerdo a lo que necesitamos, es decir, si requiero un entero lo declaro como entero, para que declararía yo un elemento como un tipo y lo usaría como otro?. No siempre se puede determinar exactamente el tipo de elemento que va a usarse en la ejecución de nuestro programa, para este tipo de casos es cuando es útil aplicar el atado posterior o ligadura dinámica, y se debe tener por lo menos una relación de herencia que por lo menos me permita determinar un tipo base para la declaración, sea cual sea el subtipo que se instancie.

Veamos un ejemplo que nos aclare un poco este concepto:


class Mamifero {
public void mover() {
System.out.println("Ahora es un mamifero el que se mueve");
}
}

class Perro extends Mamifero {
public void mover() {
System.out.println("Ahora es un perro el que se mueve");
}
}

class Perro extends Mamifero {
public void mover() {
System.out.println("Ahora es un gato el que se mueve");
}
}

public class Polimorfismo {
public static void muevete(Mamifero m) {
m.mover();
}
public static void main(String[] args) {
Gato bisho = new Gato();
Perro feo = new Perro();
muevete(bisho);
muevete(feo);
}
}


Vemos que el método “muevete” llama al método mover de un mamífero y aunque el no sabe con qué clase de mamífero trata, funciona y se llama al método correspondiente al objeto específico que lo llama (es decir, primero un gato y luego un perro). Si no existiera la ligadura dinámica tendríamos que crear un método “muevete” para los mamíferos de tipo “Gato” y otro para los de tipo “Perro”.

Veamos otro ejemplo donde las instancias se crean de forma aleatoria:


public abstract class Instrumento {
public Instrumento() { }
public abstract void tocar();
public abstract void tocar(String nota);
public abstract void afinar();
}

public class Cuerda extends Instrumento{
public Cuerda() {}
public void afinar() {
System.out.println("Cuerda.afinar()");
}
public void tocar() {
System.out.println("Cuerda.tocar()");
}
public void tocar(String nota){
System.out.println("Cuerda.tocar()"+nota);
}
}

public class Percusion extends Instrumento{
public Percusion() {}
public void afinar() {
System.out.println("Percusion.afinar()");
}
public void tocar() {
System.out.println("Percusion.tocar()");
}
public void tocar(String nota){
System.out.println("Percusion.tocar()"+nota);
}
}

public class Viento extends Instrumento{
public Viento() {}
public void afinar() {
System.out.println("Viento.afinar()");
}
public void tocar() {
System.out.println("Viento.tocar()");
}
public void tocar(String nota){
System.out.println("Viento.tocar()"+nota);
}
}

public class LigaduraDinamica {
public static void main(String[] args){
String[] notas = {"Do","Re","Mi","Fa","Sol","La","Si"};
Instrumento orquesta[] = new Instrumento[10];

for(int i=0;i<10;i++){
orquesta[i]=generarInstrumento(new java.util.Random().nextInt(3));
}

for(int i=0;i<10;i++){
afinarInstrumento(orquesta[i]);
}

for(int i=0;i<10;i++){
tocarInstrumento(orquesta[i]);
}

for(int i=0;i<10;i++){
tocarInstrumento(orquesta[i],notas[i%7]);
}
}

public static Instrumento generarInstrumento(int i){
switch(i){
default:
case 0:
return new Viento();
case 1:
return new Percusion();
case 2:
return new Cuerda();
}
}

public static void afinarInstrumento(Instrumento o){
o.afinar();
}

public static void tocarInstrumento(Instrumento o){
o.tocar();
}

public static void tocarInstrumento(Instrumento o,String nota){
o.tocar(nota);
}
}



Veamos que el método “generarInstrumento” aleatoriamente me crea las instancias de los diferentes tipos de instrumentos y que nuestro arreglo de elementos del tipo de instrumentos es llenado con diferentes elementos de los cuales no podemos controlar que tipo de instancia es creada, solo sabemos que son objetos basados en “Instrumento”, sin embargo los métodos “tocarInstrumento” y “afinarInstrumento” que reciben como parámetro un Instrumento llaman adecuadamente a los métodos “afinar” o “tocar” según la instancia que reciben.

10 comentarios:

Anónimo dijo...

Hola no pues mut buena informacion y te quedo muy bien pero quiesira saber un poco mas de polimorfismo y reutilizacion pero ya establesidos como conceptos y algunos ejemplos de ellos para enterder un poco mas..te lo agradeceria.

Anónimo dijo...

Muy bueno el artículo!! Fijate que en el ejemplo de Mamifero definis dos veces la clase Perro.

Saludos

Anónimo dijo...

Hola..esta muy xida tu informacion, te agradesco bastante me acabas de salvar de un nota, me faltaban ejemplos de ligadura dinamica y ps gracias a esto, hicistes que entendiera de la mejor manera, solo un aporte en el ejemplo de Mamifero,se extiende Gato y Perro, corrige pusistes Perro y perro..xD
Bueno sigue asi..saludoS

andrezi dijo...

muy buena explicacion gracias

Anónimo dijo...

Excelente informacion, no cabe duda que el polimorfismo pone fin a los if`s que son utilizados para identificar el comportamiento de una clase basada en estatus

Anónimo dijo...

Dudo que ponga fin, pero si que ayuda.
Muy buen articulo, me encanto.

Saludos

Anónimo dijo...

Great blоg here! Alѕo yοur web site loaԁs up fast!
What host are you using? Can I get your affilіate link tо yοur host?
I wish my web site loaded up as quicκlу as yourѕ lol

My wеbsitе - Https://Locationtech.Org

Gustavo dijo...

Excelente informacion, no cabe duda que el polimorfismo pone fin a los if`s que son utilizados para identificar el comportamiento de una clase basada en estatus.

Project Management Books

TheOnlyOne dijo...

Gracias por la info

TheOnlyOne dijo...

gracias por la info