Federico Alemany
Programación Orientada a Objetos (POO) - Parte 2
Escrito por Federico Alemany - 21 de diciembre de 2013
Lo que vimos hasta ahora en la primera parte de este post hemos visto conceptos básicos del paradigma de objetos tales como: que es una clase, que es un objeto, que es un mensaje, etc.
En esta parte, voy a explicar conceptos un poco mas avanzados, como por ejemplo la herencia y el polimorfismo, como así también que es un método, un evento, la diferencia entre método y mensaje, la abstracción, el encapsulamiento, la modularidad, la recolección de basura, entre otros.

conceptos necesarios antes de empezar

diferencia entre método y mensaje: como habíamos visto en la primera parte de este post, un método, es un bloque de código encargado de realizar alguna tarea especifica. Por ejemplo, un objeto de la clase medios de transporte podría ser un automóvil. Veamos como se compone este objeto automóvil

objeto automóvil:
Atributos:
- capacidad_de_personas
- velocidad_actual
- velocidad_maxima
- cantidad_de_ puertas
Métodos:
+ acelerar(cuantos_km/h)
+ detenerse()
+ ir_a(lugar) .....

.....como vemos en este ejemplo, el objeto automóvil esta compuesto por atributos y métodos. Como dijimos, los metodos son bloques de codigo encargados de realizar alguna tarea especifica. Para ejemplificar esto, vamos a ver el código del método acelerar(cuantos_km/h)...


Acelerar(cuantos_km/h){
Velocidad_actual = velocidad_actual + cuantos_km/h ;
}



Este método es el encargado de aumentar la velocidad del objeto automóvil, es decir, tiene un bloque de código que aumenta en "cuantos_km/h" unidades la velocidad actual del objeto.
Entonces.... Un método es una pequeña porción de código que generalmente modifica el estado interno del objeto (sus atributos), y digo "generalmente" porque podria no siempre lo hace... Un método podría simplemente ser el encargado de mostrar un mensaje por pantalla, o devolver ciertos valores, etc.
Ahora... Cual es la diferencia entre un método y un mensaje?? Es simple, un método es la implementacion de un mensaje.

Como es esto? Un mensaje es una solicitud a un objeto para que ejecute alguno de sus métodos. Cuando escribimos automovil.acelerar(32), lo que estamos haciendo es decirle al objeto automóvil que aumente su velocidad en 32 unidades, mediante la ejecución del método acelerar(cuantos_km/h). Cuando un objeto recibe un mensaje, busca entre sus métodos aquel que coincida con el nombre del mensaje, la cantidad de parámetros, y el tipo de datos de esos parámetros. Es decir, si encuentra al método que coincida exactamente con esas tres cosas, lo ejecutara. A esto se denomina firma del mensaje, o signatura del mensaje (nombre, cantidad de parámetros y tipo de datos de los mismos).

Evento
La programación orientada a objetos no necesariamente necesita de eventos. Un evento es una acción que ocurre en un momento determinado del tiempo, ya sea por acción del usuario, o porque el propio sistema lo genera. Por ejemplo, cuando hacemos click con el mouse... Eso es un evento, cuando presionamos una tecla del teclado... Eso es un evento, cuando minimizamos una ventana... Eso es un evento, etc, etc, etc.
Es así que cada acción que llevamos a cabo en nuestra computadora es tomado por la misma como un evento. Pero también existen eventos generados por ella, como por ejemplo, la finalizacion de un determinado lapso de tiempo (temporizador).
Que tiene que ver esto con la poo?? Muy simple, la poo se utiliza generalmente de manera conjunta o acompanada de la programación guiada por eventos. Cuando un evento ocurre, se invoca a un metodo determinado.
Por ejemplo, cuando hago click en el boton minimizar de cualquier ventana, se envia un mensaje al objeto ventana para que ejecute su metodo minimizar()... Mas simple imposible!

Abstracción
La abstracción es una propiedad que permite en programación, tomar información relevante, y descartar aquellos detalles que no son importantes en el ámbito del problema. Es decir, pensemos que debemos construir una clase que se llame pacientes, para una aplicación de un consultorio medico (de esos típicos que usan las secretarias para asignar turnos). Pensaríamos entonces en que propiedades de los pacientes nos interesan... Edad, peso, antecedentes médicos, altura, dirección (para enviarle una linda postal a fin de anio), y teléfono. Si observamos bien, todos estos atributos del objeto paciente son relevantes para un consultorio medico. Nos interesaría agregar el atributo ingresos_mensuales?? Y... La verdad que no, para que querría un medico tener dicha información (dejemoslo ahi...).
En conclusión, la abstracción nos permite centrarnos en los detalles inherentes al problema, dejando de lado detalles que no lo son. También se denomina abstracción a una propiedad que tienen los métodos. Cuando hablábamos del método acelerar(cuantos_km/h), sabíamos que si lo llamábamos, y le pasábamos una velocidad determinada entre paréntesis, el objeto automovil aceleraria. Pero nada sabíamos acerca de como lo hace... Es decir, la forma en que este método lleva a cabo su tarea es irrelevante (si tuviéramos un empleado que nos prepara el desayuno todas las mananas, nos daria lo mismo que preparara primero el café, y luego agregue mermelada a las tostadas o viceversa, siempre que el desayuno llegue caliente y delicioso a la cama... No es así??) Entonces, no debería importar como un método lleva a cabo su tarea, sino que la realice correctamente. El método anterior:

Acelerar(cuantos_km/h){
Velocidad_maxima = velocidad_maxima + cuantos_km/h;
}


Tranquilamente pudo haber sido asi:

acelerar(a_que_velocidad){
Velocidad_actual = a_que_velocidad ;
}


Es decir, no nos interesa la forma en que el método realiza su tarea... Lo único que nos interesa es que ese metodo acelera la velocidad del objeto automovil. A este "ocultamiento de detalles" se denomina también abstracción. Algunos autores llaman a esto encapsulamiento y a veces se suelen confundir, pero basicamente, el concepto es el mismo (o puede encontrarse con ambos nombres).

Herencia
Y por fin llegamos al concepto de herencia, uno de los mas faciles de explicar, pero que trae aparejado muchos otros conceptos. Sin entrar en poo, pensemos en el concepto de herencia (sin ser la economica). Cuando nacemos, tenemos ciertas caracteristicas, que segun nuestros padres heredamos de alguno de los dos (eventualmente de los abuelos) como por ejemplo el color de ojos, el caracter, la altura, ciertas destrezas, etc.
En poo sucede lo mismo con las clases de objetos... Ciertas clases heredan de otras, metodos y atributos, conformandose asi una jerarquia de clases... Pero vamos despacio: pensemos en la clase mediosdetransporte:

Mediosdetransporte{

Atributos:
- velocidadmaxima;
- capacidadpersonas;
- autonomia;
- tipocombustible

Métodos:
+ acelerar(cuantos_km/h)
+ detenerse()
+ ir_a(lugar)
}

Esta clase servirá como clase base o clase padre para otras. Esto quiere decir que podemos hacer otras clases que contengan todos los metodos y atributos que la anterior, pero que ademas tengan métodos y atributos propios. Veamos dos ejemplos de clases que hereden de la clase mediosdetransporte...

Caballo{

Atributos:
- velocidadmaxima;
- capacidadpersonas;
- autonomia;
- tipocombustible
- raza
- edad

Métodos:
+ acelerar(cuantos_km/h)
+ detenerse()
+ ir_a(lugar)
+ comerpasto
+ dormir
}


Motocicleta{

Atributos:
- velocidadmaxima;
- capacidadpersonas;
- autonomia;
- tipocombustible
- marca
- cilindrada

Métodos:
+ acelerar(cuantos_km/h)
+ detenerse()
+ ir_a(lugar)
+ realizarmantenimiento
+ encenderluces
}

Creo que en el ejemplo quedo mas que claro... La herencia se utiliza para dos cosas fundamentales. Primero, para ahorrarse el trabajo de programar o codificar dos clases que tienen los mismos metodos y atributos, haciendolo solo una vez en la clase padre, y luego declarando en las clases hijas que heredan de su padre. Con esto se consigue que una clase tenga los mismos metodos y atributos que su padre, pero que ademas pueda agregar los suyos. La otra finalidad que persigue la herencia es una correcta organizacion en la jerarquia de clases, haciendo mas entendible el modelado del problema.

aclaremos ciertas cosas:
Cuando una clase padre tiene un metodo (por ejemplo acelerar(cuantos_km/h)), la clase hija puede modificar varias cosas: por un lado, puede dejar intacta la firma del metodo (nombre y cantidad y tipo de parametros) pero modificar su funcionamiento interno, es decir, cambiar la forma en la que el metodo cumple con su tarea. Este mecanismo se conoce como especializacion debido a que las clases hijas, especializan la forma de actuar de los metodos heredados.
Por ejemplo, si en la clase padre mediosdetransporte existe un metodo denominado cargarcombustible... Pensemos que si una clase hija llamada automovil debe hacerlo, debera incrementar el numero de litros de combustible en su deposito, mientras que si pensamos en una clase hija llamada caballo, deberiamos incrementar el numero de kilogramos de pasto en su estomago (que ocurrente el ejemplo no? :) ). Es asi que las clases hijas que heredan de sus clases padres pueden especializar ciertos metodos en funcion de las necesidades especificas de cada una de ellas.
Ademas de modificar solo el funcionamiento de un metodo heredado, las clases hijas tambien pueden modificar la cantidad y/o el tipo de parametros que recibe dicho metodo, manteniendo intacto el nombre del mismo. A este metodo se lo denomina sobrecarga de metodos, y se da cuando una clase hija cambia la cantidad y/o los tipos de parametros que este recibe.pueden existir en una clase dos metodos con el mismo nombre, pero ambos difieren en el tipo y/o cantidad de parametros que reciben, entonces se dice que ese metodo esta sobrecargado. Cuando un objeto recibe un mensaje, buscara entre sus metodos (y ejecutara) aquel que coincida en el nombre, pero ademas en el tipo y la cantidad de parametros.supongamos que un objeto recibe un mensaje cualquiera...
automovil.acelerar(20km/h)... Que sucede si este objeto no cuenta con un metodo que coincida en el nombre o en el tipo y cantidad de parametros?? Buena pregunta no? Lo que sucede en este caso es que el objeto empezara a buscar entre sus propios metodos, y si no lo encuentra lo buscara entre los metodos de su clase padre... Y si no lo encuentra buscara entre los metodos de su clase abuelo... Y asi sucesivamente hasta llegar a la clase raiz. En caso de no encontrar el metodo, devolvera un error diciendonos que ese metodo no ha sido encontrado. A este mecanismo de busqueda hacia arriba en la jerarquia de clases se denomina look-up o simplemente "busqueda hacia arriba".

Modularidad
Este concepto es mas sencillo, y viene del viejo dicho "divide y triunfaras". Se trata de un concepto que implica dividir el gran problema en muchas partes pequenas, a fin de que cada una de ellas sea mas facil de manejar y sea o este especializada en una tarea particular. Pensemos en la construccion de un edificio, donde cualquier persona hace lo que quiere... El arquitecto colocando los ceramicos, el electricista pintando las paredes... Seria un completisimo desastre no? En la poo pasa lo mismo. Cuando modelamos una serie de clases de objetos, con sus respectivos metodos y atributos, debemos pensar en dividir el problema en muchas partes pequenas... Siguiendo el ejemplo del edificio deberiamos pensar en una clase "obreros" y dentro de ella subclases o clases hijas que se encarguen respectivamente de colocar ceramicos, otra de preparar el cemento, otra de armar las columnas, etc... Mientras otra clase electricista tendra subclases que se encargaran de pasar cables, otras de colocar lamparitas, etc. Este concepto nos estimula a organizar las clases para que el modelado sea: mas entendible, mas util, y con menor redundancia, logrando el desempeno optimo y una mejor especializacion de cada clase.

Recoleccion de basura
Tambien denominado garbage collector, es un metodo de "limpieza de memoria". Cuando corremos una aplicacion que respeta el paradigma de objetos, cada objeto que es creado durante la ejecucion va a parar a memoria principal... Entonces, que pasaria si ejecutamos nuestra aplicacion un tiempo prolongado?? La respuesta mas sencilla seria "la memoria se va a llenar y ya no se podran seguir cargando objetos en ella". Es por esto, que existe lo que se denomina recolector de basura, y consiste en un mecanismo de eliminacion de objetos que ya no se utilizan en memoria con el objeto de optimizar el uso de la misma.

Polimorfismo
Que raro suena esto... No?
Aunque suene raro, su definicion es bastante simple... El polimorfismo es la capacidad que tienen objetos de diferentes clases de responder a mensajes con el mismo nombre. Ejemplifiquemoslo:

Una clase caja_ahorro: posee el metodo
extraerdinero(cuanto)
Una clase cuenta_corriente: tambien posee el metodo
extraerdinero(cuanto)

Tengamos en cuenta que las dos clases son totalmente distintas. Solo a modo de ejemplo vamos a decir que cuando extraemos dinero de una caja_ahorro se resta el monto que extraemos del dinero disponible en ella (como lamentablemente sucede en la realidad)... Mientras que si extraemos dinero de una cuenta_corriente, ademas de restarse el dinero que extraemos, el banco nos cobra una comision de $10. Vamos a ver como podrian ser estos metodos:

-----------------------------------------------------------------------------------------------------
Metodo extraerdinero(cuanto) de la clase caja_ahorro:

extraerdinero(cuanto){
Saldodisponible = saldodisponible - cuanto;
}

-----------------------------------------------------------------------------------------------------

Metodo extraerdinero(cuanto) de la clase cuenta_corriente:
extraerdinero(cuanto){
Saldodisponible = saldodisponible - cuanto - 10;
}

-----------------------------------------------------------------------------------------------------

Ahora, supongamos que tenemos que hacer el balance diario de los movimientos de todas las cuentas del banco, y para ello, vamos leyendo de un archivo una por una las cuentas (ya sean cajas de ahorro o cuentas corrientes).

He aqui donde aparece el polimorfismo.... Primero leo una cuenta a la vez del archivos de cuentas y lo guardo en un objeto...

nuevacuenta = cuentas.leersiguiente()

Y ahora magicamente puedo hacer lo siguiente:

nuevacuenta.extraerdinero(cuanto)

La pregunta es... Como puedo saber si la cuenta que lei del archivo es una caja_ahorro o una cuenta_corriente??

La respuesta es simple: no me interesa!! Porque ambas clases poseen el metodo extraerdinero(cuanto), mas alla que cada una de ellas haga cosas distinas. Entonces, el polimorfismo, como dije antes, es la capacidad que tienen objetos de diferentes clases de responder a mensajes con el mismo nombre, lo cual es extremadamente util en ciertas ocaciones (como en el ejemplo anterior).

Un mensaje con la misma firma puede ser entendido por objetos de distintas clases, aunque cada uno de ellos tenga un metodo asociado distinto. Recordemos que un metodo es la implementacion de un mensaje, por lo tanto cada una de las clases tendra maneras distintas de implemetar dicho mensaje, pero con seguridad sabemos que lo entendera.

Bueno, con esto creo que queda bastante mas claro y mas completo el primer post. De todas maneras, y como dije antes, cualquier consulta no duden en preguntarme en los comentarios y respondere alli, de tal manera que todos puedan ver la pregunta y la respuesta. Espero haberme explicado bien y que les haya servido.

Que son las funciones
Las funciones son bloques de codigo encargados de realizar una tarea determinada, y como resultado devuelven un valor. Este valor puede ser numerico, una cadena de texto, de tipo booleano, etc.
Un claro ejemplo de como funciona una funcion (valga la redundancia) es el de calcularedad()
Suele suceder que entre los datos que manejamos en una aplicacion tengamos la fecha de nacimiento de un cliente, paciente, etc. Sabemos que esos datos van a permanecer en nuestra aplicacion durante un tiempo bastante prolongado, por lo que guardar la edad no seria factible, ya que al pasar un anio, dicho dato seria erroneo, y tambien seria tedioso tener que actualizar la edad de cada cliente de nuestra base de datos una vez por anio. Para casos como este (y muchos otros) existen las funciones, que como dije antes, reciben ciertos parametros (o argumentos), realizan cierto procesamiento y devuelven un resultado que puede ser de algun tipo de datos en particular. Veamos este ejemplo...

Datos de nuestro cliente:
Nro. De cliente: 100
Nombre: federico alem
Domicilio: chubut 1918
Fecha de nacimiento: 17/06/1986

En alguna parte de la aplicacion supongamos que queremos mostrar la leyenda

El cliente nro. Xxx se llama xxxx, vive en xxxxx y tiene xx anios.

La pregunta seria... Como calculo la edad? Para ello disponemos de las funciones. Esta podria ser:


Public function calcularedad(byval fecnac as date) as integer
Dim edad as integer
Edad = date.today().year - cliente.fecnac().year
Return edad
End function


O lo que podria ser mas corto...

Public function calcularedad(byval fecnac as date) as integer
Return (date.today().year - cliente.fecnac().year)
End function


(en el ejemplo use la sintaxis de visual basic... Hace un tiempo que no programo en ese lenguaje asi que perdon por algun posible error de sintaxis, pero creo que la idea se entiende)

Notese que en el primer ejemplo, se define una funcion que se llama calcularedad y como argumento recibe por valor (esto lo explico mas adelante) una variable de tipo date (o fecha). De este valor, que posiblemente tenga esta forma dd/mm/aaaa se rescata solo la parte final (aaaa), es decir el anio de nacimiento. La resta entre este valor y el anio actual se guarda en una variable de tipo integer (del mismo tipo de dato que el valor que devuelve la funcion). Luego obviamente se lo retorna mediante return.
Sin embargo, en el segundo ejemplo no se define una variable, por lo que el tipo de dato se "induce", es decir, si resto un numero entero con otro numero entero, el resultado sera... Y si!!! Otro numero entero. Hay que tener cuidado de retornar siempre el mismo tipo de dato que se declaro en la firma de la funcion, ya que pueden pasar dos cosas:

1) el tipo de datos que intentamos retornar es el mismo que el declarado en la funcion... Todo funciona

2) el tipo de datos que intentamos retornar no es el mismo que el declarado en la funcion... En este caso, el compilador determinara si es posible convertir el valor que estamos intentando retornar a integer (esto funciona si le pasamos un double, single o hasta un string con numeros), pero si le pasamos otro tipo de datos no convertible a enteros, nos dara error... Ojo con eso!!

Procedimientos: paso por valor y por referencia
Cuando llamamos a un procedimiento, generalmente le pasamos valores como argumentos. Estos pueden ser por valor o por referencia. Cual es la diferencia? (me salio un verso sin esfuerzo... Jaja estoy re chistoso hoy)

Supongamos la siguiente situacion... Tenemos un objeto llamado cliente con atributos nombre y edad
Este objeto esta almacenado en memoria principal
Si llamamos a un metodo y le pasamos el atributo edad por valor, se creara una copia del valor del atributo edad en memoria (es decir, en otro lugar de memoria) por lo que cualquier cambio que hagamos con ese valor, no afectara al valor real del atributo edad, ya que estamos manejando una copia de ese valor en memoria. Por el contrario, si pasamos el valor del atributo edad por referencia, lo que hacemos realmente es pasar como argumento la direccion fisica de donde se encuentra el atributo edad, por lo que cualquier cambio que realicemos a este valor tambien afectara o hara cambiar el valor del atributo. Espero haberme explicado correctamente.
Cualquier duda... Ya saben.

Comentarios:

Nombre:
Comentario: