Polimorfismo
El polimorfismo es quizá al característica más importante de la programación orientada a objetos y también quizá la más difícil de entender y creo que en parte se debe al nombre; no ayuda mucho y si impresiona al principio.
Pero si en vez de pensar en la palabra pensamos en como los objetos se comportan de diferente forma dependiendo de su naturaleza puede ser más claro.
Por ejemplo, en el post anterior Clases y Objetos hablaba como los objetos de nuestros sistema se pueden clasificar de acuerdo a sus atributos y comportamiento. Los objetos de la misma clase se comportan igual y los objetos de diferentes clases se comportan diferente. ¿Parece demasiado obvio verdad? Pues es que lo es.
Por ejemplo, el mecanismo de persistencia ( entiéndase, cuando voy a "salvar", o "guardar" algo ) puede utilizar una base de datos o el sistema de archivos entonces se tendrían dos clases diferentes donde cada una hiciera algo distinto.
Si tuvieramos esta clase llamada Persona:
Podríamos escribir una clase que persistiera personas a una base de datos así:
void salva( Persona persona ) {
SomeMagicLibrary.executeUpdate(
"UPDATE persona SET nombre = ? , apellido = ? ",
persona.nombre(),
persona.apellido() );
}
}
El código para salvar a la base de datos no importa mucho en este ejemplo, basta con ver que se utiliza una sentencia SQL y por ahí una biblioteca mágica que haga todo el trabajo.
Podríamos también tener otra clase que en vez de usar una base de datos escribiera a un archivo:
void salva( Persona persona ) {
PrintStream out = new PrintStream( new FileOutputStream( persona.nombre() + ".txt") );
out.printf("{ 'nombre' : '%s', 'apellido' : '%s' }", persona.nombre(), persona.apellido() );
out.close();
}
}
De nuevo la implementación aquí no es la importante, sino que tenga un método que recibe una instancia de persona y hace algo.
De esto se trata el polimorfismo! de que diferentes clases responden de diferente forma al mismo mensaje ( mensaje es cuando se invoca o se ejecuta un método ), en este caso el mensaje o el método fue salva( Persona )
y las dos clases lo hicieron de forma diferente. Eso es todo.
Ahora. En Java, el tipo de dato ( la clase ) del objeto se debe de declarar antes de que el programa se ejecute ( por eso se le llama de tipeo estático, por que los tipos de datos se definen "en frio" )
Persona juan;
Otros lenguajes permiten definir el tipo de dato en tiempo de ejecución ( tipeo dinámico )
Como a los objetos solo se les pueden enviar mensajes que estén definidos en su propia clase, habría problema para intentar hacer esto:
...
GuardaPersonaEnBaseDeDatos guardar = new GuardaPersonaEnBaseDeDatos();
guardar.salva( juan ); // lo almacena en la base de datos.
guardar = new GuardaPersonaEnArchivo(); // eeerrrk!! Error en tiempo de compilación
guardar.salva( juan ); // ya ni siquiera llega a esta línea.
El problema es que la variable guardar
fue declarada como de tipo GuardaPersonaEnArchivo
y por lo tanto ya no se le puede asignar el tipo GuardaPersonaEnArchivo
En Java existen dos formas de solucionar este problema, la primera es definir una clase padre para ambas clases y definir el tipo de dato como el de esa clase:
void salva( Persona persona ) {
// nah... no hagas nada..
}
}
Y luego
void salva( Persona persona ) {
. .. . .
}
}
y
void salva( Persona persona ) {
. .. . .
}
}
Y definir el tipo de datos como el del padre:
...
Guardador guardar = new GuardaPersonaEnBaseDeDatos();
guardar.salva( juan ); // lo almacena en la base de datos.
guardar = new GuardaPersonaEnArchivo(); // compila bien
guardar.salva( juan ); // ejecuta el otro método
Y dadaaaa!!! ya tenemos polimorfismo.
La otra opción es implementando una interfaz pero ya veremos eso después.
Conclusión
Si parece demasiado fácil hacer polimorfirsmo en Java y se quedan con la idea de "Que!! eso es todo?!" pues les diré que sí, eso es todo.
Lo interesante aquí viene en la forma en la que se utiliza el polimorfismo y porque es tan importante en la POO.
Por ejemplo en los plugins se escribe código y se llaman métodos sobre clases que ni siquiera existen aún, porque será el autor del plugin el que provea esa nueva funcionalidad.
De la misma forma, la gran mayoría de los famosos patrones de diseño se basan precisamente en esto, en el polimorfismo para que sean las implementaciones las que digan que es lo que se va a hacer.
Y en el ejemplo mismo que es tan burdo, se puede agregar fácilmente quizá un año después de haber escrito este pedazo de código, un nuevo "Guardador" que utilice la red en vez de la base de datos o el sistema de archivos, bastaría definir el nuevo método:
void salva( Persona persona ) {
Socket s = new Socket( someServer, somePort );
DataOutputStream dos = new DataOutputStream( s.getOutputStream() );
dos.writeUTF( persona.nombre() );
dos.writeUTF( persona.apellido() );
dos.close();
}
}
Con lo que tendríamos un nuevo guardador. Y eso es todo.
¿Preguntas?
- OscarRyz's blog
- Inicie sesión o regístrese para enviar comentarios
Comentarios
El polimorfismo es el que le
El polimorfismo es el que le da POWER a la OOP.
Fácil de entender, difícil de digerir
+1 por tu post, es bastante entendible lo que explicas y muy fácil de entender, pero cuando lo quieres llevar a la práctica (al menos a mi me pasa) es cuando es difícil de digerir ya sea porque no estás acostumbrado a programar de esa forma o por que estas empezando a programar. Y ya que lo mencionaste tengo una duda, sí la clase
Guardador
no tiene código en su método, ¿podría ser una interfaz? Es decir ¿puedo hacer polimorfismo con interfaces?Un poquitin de abstraccion
En este caso, veo tambien la importancia de resaltar que existe eso de las clases y metodos abstractos. En el ejemplo de oscar se pudo haber definido una interface pero tambien una clase abtracta que defina los metodos que deben de rellenarse para que la implementacion tenga un comportamiento particular.
Me explico:
public abstract void salva();
}
Definimos una clase
Guardador
que nos dice que tenemos un metodo abstractosalva()
el cual, por ser abstracto lo debemos definir en la clase que herede aGuardador
para que de esa forma el comportamiento sea diferente de todas esas implementaciones del metodo salva()Lo que menciono quedaria asi
@Override
public void salva () {
// logica del metodo
}
}
Como estamos haciendo herencia de
public void salva ();
debemos definir este metodo (con el mismo modificador de acceso, porque asi se definio en la clase abstracta... queda de tarea investigar el ¿por qué?) y dentro de la sobreescritura del metodo ahi es donde le damos la particularidad del comportamiento.Si deseamos tener varias clases con los mismos metodos y que puedan ser declaradas bajo un mismo tipo, esta solución también es viable e incluye el concepto de polimorfismo. Dicho lo anterior entonces sabemos que podemos hacer esto:
Para el desarrollo de plugins se maneja mucho el uso de interfaces que va tambien implicito el polimorfismo... "practicamente" es lo mismo que abstraccion, tiene mas limitaciones su uso en definicion de propiedades y de modificadores de acceso, ¿a ver quien se avienta a complementar el ejemplo de Oscar usando interfaces?
ejemplo mas complejo
Polimorfismo: "Llamada a un Java / método virtual utilizando una referencia a una superclase más generalizado de un objeto real invoca el método en el objeto real (el más específicos de subclase), utilizando un bottom-up mecanismo de búsqueda". Noooo pues con estas definiciones si le voy a entendeeeer, al menos con esta definicion me hago mas bolas. y luego este ejemplo que encontre:
//un objeto de tipo gato
Cat simon = new Cat ();
//se crea un objeto de tipo animal, que extiende de gato ???
Animal animal = simon; /* upcasting */
animal.eat (); /* = Cat.eat */
Perro rover = new Perro ();
/* Rover segura upcast referencia a los animales*/
feed(rover);
feed(simon); //aqui se esta mandando nuevamente al objeto gato
)
void feed(Animal a)
(
// Animal.eat () = Cat.eat ()
// feed(rover) = Dog.eat ()
//feed(Simon) = Cat.eat ()
a.eat ();
)
Sinceramente no entiendo si se hace las mismas operaciones en
animal.eat();
y cuando se manda al metodo y se ejecutaa.eat();
No dudo que para muchos se les complique entenderlo, pero tambien a otros como yo pues si se nos dificulta y mas con este tipo de ejemplos.
Hablando de sobreescritura...
Hace unos días tenía que hacer un progressbar que se actualizara conforme un proceso iba avanzando y ezamudio me recomendó utilizar el StringWorker, que mejor ejemplo para ilustrar lo que java.daba.doo menciona ya que tu clase debe extender de la clase StringWorker y pasarle dos parámetros que puede ser de cualquier tipo,
public class MiClase extends SwingWorker<String, Void>
y debe sobrescribir el métododoInBackground
, ¿no sé si eso también sea polimorfismo?Yo te recomendaria usar
Yo te recomendaria usar interfaces, a excepcion de algunos casos en donde te recomendaria clases Abtractas:
void salva( Persona persona );
}
void salva( Persona persona ) {
Socket s = new Socket( someServer, somePort );
DataOutputStream dos = new DataOutputStream( s.getOutputStream() );
dos.writeUTF( persona.nombre() );
dos.writeUTF( persona.apellido() );
dos.close();
}
}
Muy bueno, pero algo confuso para novatos
Cuando antes trataba de explicarle a alguien nuevo a la POO comenzaba con ejemplos cómo el que tú das. Al final siempre terminaban preguntándome: "Bueno, a fin de cuentas polimorfismo es..." y con una carota de: "WTFFFF?!?!?!?!?!".
A mi parecer una manera más sencilla de hacer entender el polimorfismo es la manera en que llamas a un mensaje...aunque hay diferencia de opiniones respecto de los operadores (hablando de Java) en si se pueden o no considerar cómo mensajes. Sin embargo ilustran de una manera el polimorfismo. Ejemplo:
class EjemploSignoSuma{
---public static void main(String ... args){
------//Si usamos el signo + con un entero, lo suma:
------int x = -15;
------int y = 665;
------int z = x + y;
------String s = String.format("Usando el símbolo de suma obtenemos que %d + %d = %d", x, y, z);
------//Ahora un ejemplo del uso del símbolo + con cadenas
------String wordOne = "The only place";
------String wordTwo = "where you can dream";
------s += "\n La frase usando el símbolo + es: " + wordOne + wordTwo;
------System.out.println(s);
------System.exit(0);
---}
}
Y lo vemos fácil, pues para cadenas el símbolo + es igual a decirle: "concatena", y para números es igual a decirle: "suma". Otro ejemplo de polimorfismo con este mismo símbolo es el uso de expresiones regulares (las cuales, un servidor no domina muy bien, de hacer una metida de pata corregirme por favor), en donde este mismo símbolo (+) sirve para indicar que debe haber uno o más dígitos de un carácter indicado.
Bueno, ya está esto bastante largo, hasta ahí lo dejo.
Excepto que + es un operador
Excepto que
+
es un operador en Java, así que no hay objetos involucrados. :(Re: Muy bueno, pero algo confuso para novatos
Eso es sobrecarga, de operadores.
Re: Excepto que + es un operador
Cierto, quizás no aplica para Java. Esto lo podríamos ilustrar puramente cómo objetos en Ruby, Scala; en donde todo es un objeto. Sin embargo es una manera sencilla de explicarlo.