Referenciación de clases
Los lenguajes de programación orientados a objetos poseen una particular sintaxis
de referenciación denominada sintaxis de puntos. El nombre lo recibe ya que un
punto (.) separa la referencia del objeto (instancia) de aquello que desea hacerse con
él (manipular sus propiedades o métodos).
mi_CD_player.cambiarVolumen(80);
El script está indicándole al objeto mi_CD_player que modifique su volumen a través
del método cambiarVolumen().
En el caso de instanciar un objeto dentro de un segundo, las reglas sintácticas de comunicación
continúan con la misma nomenclatura:
mi_barrio.mi_casa.mi_cuarto.mi_CD_player.cambiarVolumen(80);
Como el objeto mi_CD_player se encuentra anidado dentro de otros, es preciso recorrer
todo el árbol para poder referenciarlo.
Herencia
La herencia entre objetos es un concepto que nos permite definir un nuevo objeto a partir de las cualidades físico-actitudinales (propiedades y métodos) de un segundo más genérico. Esta característica, junto con la de polimorfismo, nos ahorra un tiempo considerable a la hora de crear objetos que guardan similitud con otros.
En el caso del reproductor de CD, podemos optimizar dicha clase y hacerla más escalable creando una nueva y más extensa clase audio_player que resulte apta para la generación de reproductores de casetes, vinilo, minidisc, CD o cualquier otro formato. Con esto en mente, la clase CD_player heredaría las generalidades de la clase audio_player, particularizando la propiedad dispositivo_de_reproduccón a un CD. Si posteriormente deseamos crear un reproductor de vinilo o cualquier otro formato que aún no esté en el mercado, el molde creado seguirá siendo útil.
Polimorfismo
Es una característica de OOP por la cual podemos definir un mismo método en varios objetos, pero cada uno de ellos con distintos comportamientos. Dicho de otro modo, es la posibilidad de codificar métodos con el mismo nombre en clases distintas. En el caso del reproductor de CD que veníamos explicando, podríamos obtener una copia del método play() de un reproductor de VHS. Sin embargo, como el reproductor de CD no posee la aptitud de emitir imágenes, dicha acción debería “ajustarse” a las capacidades específicas del equipo de CD.
Anidación
La anidación de objetos consta de establecer relaciones jerárquicas entre ellos. Según su posición dentro del esquema, sus acciones repercutirán en mayor o menor grado sobre el resto. Dichas relaciones establecen estructuras del tipo “árbol”, donde el último eslabón se transforma en la “hoja” de una serie de “ramas”. Si la hoja se cae, no afectará el resto de la estructura, pero toda rama que se rompa llevará consigo
las ramificaciones que le suceden.
Veamos el caso de la instalación de un nuevo reproductor de CD dentro del dormitorio de un individuo: el equipo de música estará dentro de un cuarto, que a la vez pertenece a una casa, y ésta es parte de un barrio (barrio\casa\cuarto\reproductor_cd).
Si se produce un cortocircuito en el reproductor y se quema su fuente, el hecho no repercutirá ni en el dormitorio, ni en la casa, ni el barrio. Sucede lo contrario si hay un incendio y la casa es consumida por las llamas. Sólo el barrio quedará de pie; tanto el reproductor como el cuarto desaparecerán junto con la casa.
Clases
El primer paso en el desarrollo de una aplicación OOP consiste en definir “moldes” o “clases”. Éstos llevan consigo las especificaciones descriptivas y actitudinales (métodos y propiedades) que luego serán transmitidas a sus descendientes. Paso siguiente, un constructor determina la creación de un objeto a partir de ese molde y especifica sus características particulares en un proceso denominado instanciación.
Veamos un ejemplo para ser más claros.
Un programador genera la clase CD_player conjuntamente con la definición de sus métodos y propiedades. Cada unidad (objeto) creada a partir de este molde (clase) realizará las tareas que le fueron asignadas en distintos momentos e intensidades (play, stop, pause, eject, subir y bajar volumen), podrá recibir una apariencia específica combinando sus propiedades (color de cobertura, forma de los botones, color del display, etc.) y será totalmente independiente de sus pares.
A este objeto sólo le resta ser individualizado mediante un único e irrepetible nombre de instancia.
Supongamos que hemos definido el método cambiarVolumen() y la propiedad volumen (entre otras) en la clase CD_player. Una vez que la clase asigna su estructura a un objeto denominado mi_CD_player (proceso de instanciación llevado a cabo a través de un constructor), el objeto se vuelve receptor de órdenes.
De esta manera, cuando el programador indique mi_CD_player.cambiarVolumen(80), el reproductor de CD modificará su propiedad volumen y
la audición del sonido emitido por el objeto cambiará al 80% de su capacidad Es sólo a través de los métodos (al menos, la corriente purista de OOP así lo sostiene) que el entorno del objeto puede relacionarse con los datos encontrados dentro de él. Esta particularidad es justamente la que marca la “independencia”, “transportabilidad”, “facilidad” y “reutilización” de este esquema de programación: no existe código disperso por la aplicación; todo está correcta y prolijamente encapsulado dentro de unidades “clase” y “objeto”. Una vez que una clase ha sido creada, no precisamos saber qué ocurre dentro, tan sólo debemos conocer la sintaxis para invocar sus interfases o métodos.
Programación orientada a objetos
Tiempo atrás, el universo de los lenguajes de programación estaba reinado por el paradigma de la programación estructurada: rara vez se escuchaba nombrar un lenguaje que no fuera “C” en el contexto sociolaboral de un programador. Sin embargo, con el pasar de los años y el surgimiento de nuevos procesos de negocios, los programadores comenzaron a darse cuenta de que este modelo presentaba ciertas dificultades en torno a los problemas que sus desarrollos debían solucionar:
• Escalabilidad: las aplicaciones debían ser prácticamente rescritas cuando el programador
intentaba agregarle nuevas funcionalidades no planificadas en el momento
de su génesis.
• Dificultad: las aplicaciones terminaban formadas por miles y miles de funciones
que impedían una ágil y sencilla lectura del código fuente.
• Disgregación: falta de una lógica explícita que vinculara el accionar de los diferentes
módulos o funciones entre sí.
• Implementación: toda modificación en el código fuente requería, casi sin excepción,
expertos programadores que comprendieran “C” a la perfección.
• Actualización: demandaba muchas horas hombre la realización de pequeñas actualizaciones,
también conocidas como updates.
• Reutilización y transportabilidad: el código escrito para una aplicación rara vez
servía para ser aplicado en una segunda.
La programación orientada a objetos (OOP, siglas de Object Oriented Programming) surge para salvar todas estas problemáticas. OOP plantea el siguiente paradigma: el universo se encuentra repleto de objetos independientes que interactúan entre sí.
Cada objeto posee características propias y puede comportarse de tal o cual manera según sus condiciones físicas se lo permitan. En otros términos, cuando el programa dor desarrolla una aplicación, define sus propios objetos, describe su apariencia y las relaciones existentes entre sí. El accionar en conjunto de ellos es lo que permite la ejecución del programa y la consecuente solución de los distintos problemas.
Según el esquema estructural OOP, los objetos constan de tres partes fundamentales:
• Nombre: identificación única.
• Propiedades: características físicas y compositivas.
• Métodos: acciones/tareas capaces de realizar.
Un objeto mantiene sus características ajenas al mundo que lo rodea (“encapsulación”), lo que permite una total independencia de sus pares; cada objeto existe por sí sólo. Es a través de sus métodos, también denominados interfases, que se relaciona con el mundo exterior y lleva a cabo determinadas consignas
Propiedades y comportamiento de los objetos
Lo primero que se nos ocurre preguntar es: ¿qué es un objeto? La respuesta es muy simple, bastará con mirar a nuestro alrededor para ver que estamos rodeados de objetos. Y extendiendo sólo un poco más la idea, notamos que a estos objetos les asignamos sustantivos para poder nombrarlos y diferenciarlos unos de otros. Por ejemplo, si vamos a un aeropuerto, veremos aviones, automóviles, taxis, etc. Todos
éstos son objetos; sin embargo, si observamos más detalladamente, notaremos que estos objetos tienen propiedades o atributos en común: poseen motor, ruedas y asientos, pero también se diferencian; por ejemplo, el avión puede volar y el auto no. Es decir, vemos que los objetos tienen un comportamiento propio. De esta forma podemos definir a un objeto como una entidad compleja provista de propiedades (datos, atributos) y comportamiento (funcionalidad, métodos). Tomando en cuenta estas características, es conveniente afirmar que también representan
objetos reales del mundo que nos rodea y palpamos cotidianamente.
Cada objeto expone una interfaz a otros objetos que especifica cómo éstos pueden interactuar con él, cómo pueden comunicarse con él. Esta interfaz está dada por un conjunto de métodos; así es como la interfaz del automóvil estará formada por los métodos “arranca”, “frena”, “dobla”, etc., a través de los cuales podemos interactuar con el objeto.
Por otro lado, cabe aclarar que el comportamiento es exclusivo del objeto; si bien algunos objetos a simple vista son iguales, internamente pueden ser muy distintos.
Por ejemplo, si aprendemos a manejar en un automóvil con motor diesel, podremos sin ningún problema manejar uno que funcione con nafta. Es decir, los dos objetos se nos presentan de igual forma, pero es indiscutible que los motores son muy distintos. Esto se conoce como encapsulamiento: los objetos presentan la misma interfaz pero ocultan información de su funcionamiento.
Gracias al encapsulamiento se puede cambiar un objeto por otro que presente la misma interfaz, y todo debería funcionar igual.
En segundo lugar vale mencionar que el encapsulamiento protege también al objeto de usos indebidos e inapropiados. Volvamos al ejemplo anterior para detallar un poco más esta situación. El automóvil utilizará otros mecanismos para llevar a cabo su interfaz, como, por ejemplo, abrir válvulas, mover pistones, etc., que no les permitirá usar a otros objetos.
Por lo general, cuando definimos un objeto lo hacemos en función de otros objetos conocidos. Si alguien nos habla de un objeto en particular cuyo nombre no conoce, instantáneamente empezará diciendo “es como…” para describirlo y luego deberá agregar las características particulares del objeto que desea que identifiquemos. En el ejemplo del automóvil, podría decir “es como un taxi, pero más chico”, o “es
como una moto, pero tiene cuatro ruedas”… Es decir que define al automóvil a través de objetos de similares características. Sin darnos cuenta hacemos clasificaciones; ahora, generalizando, nos damos cuenta de que todos son transportes.
En el paradigma de objetos esto se conoce como herencia, y sirve para no tener que definir comportamientos de forma repetitiva.
Desarrollemos el ejemplo; en nuestro caso tendremos un objeto transporte, que tendrá propiedades como cantidad de pasajeros, cantidad de puertas, etc., y métodos como anda, frena y dobla. De esta manera definiríamos un automóvil como un transporte, agregando las particularidades del automóvil que no estén definidas en transporte.
Ahora que sabemos agrupar los objetos en clases, podríamos agregar más objetos a nuestro modelo. Para seguir con el ejemplo, pensemos en el objeto avión: sin lugar a dudas, es un transporte; es decir que las clases simplemente son conjuntos de objetos que comparten propiedades y comportamientos.
Al agregar el objeto avión definido como un transporte, heredará de éste las propiedades (cantidad de pasajeros, cantidad de puertas) y los métodos (anda, frena y dobla). Si explotamos aún más el ejemplo, notamos que no es lo mismo.
hacer andar un automóvil que un avión, de forma tal que necesitamos agregar el método anda en automóvil, para que ruede, y en avión para que vuele. Esto se denomina polimorfismo, y nos permite tener muchas formas de comportamiento; o sea que la referencia al método anda producirá el comportamiento correcto según el objeto al que se lo esté ordenando.
Para ordenarle a un objeto que haga algo, debemos mandarle un mensaje. A través de los mensajes establecemos la comunicación entre los objetos de forma tal que les ordenamos ejecutar un método con algunos parámetros. En la Figura 2 veíamos que el conductor le ordenaba al auto que arrancara; esta orden se la daba a través de un mensaje.
Revisando el ejemplo del transporte, el automóvil y el avión, notamos que el gráfico evidencia una jerarquía: a la cabeza está el transporte, y de éste cuelgan el avión y el automóvil. En este paradigma, esto se denomina jerarquía de herencia . Notamos, también, que dicha jerarquía siempre tiene forma de árbol. Entonces encontramos una nueva agrupación de nivel superior. En resumen, los objetos se agrupan en clases, y las clases, en árboles, siempre y cuando reflejen un comportamiento común.
El automóvil, aparte de ser un objeto y además de ser un transporte, también está compuesto por más objetos con comportamientos distintos. Es decir, el automóvil está compuesto por un volante, una radio, pedales, una caja de cambios, etc.
Pero, a su vez, la radio tiene otros componentes, como display, botones, etc. Al igual que en otros ámbitos, aquí nos encontramos ante una jerarquía de elementos. Ésta se denomina jerarquía de composición, y sirve para representar que uno o varios objetos están dentro de otro que los contiene.
Para finalizar, si tenemos un solo objeto automóvil, no significa que nuestro programa podrá tener un solo automóvil. Para entender esto hay que agregar el concepto de instancia, que nos permite crear la cantidad de automóviles que deseemos. Por ejemplo, si nuestro objeto automóvil tiene una propiedad color, podemos tener instancias de automóvil con la propiedad color en rojo, azul o verde. Para lograr esto, cada objeto deberá tener un método, que nos permita crear una instancia de éste.
EL PARADIGMA DE PROGRAMACIÓN
Cuando usamos una computadora, lo que estamos buscando es simplemente una
solución a un problema, y para que resuelva dicho problema debemos decirle cómo
hacerlo, ya que, en rigor de la verdad, las computadoras no pueden más que sumar
bits y mover bytes de un lugar a otro. Nuestra tarea como programadores es, entonces,
indicarle a la computadora qué es lo que queremos que haga; para ello debemos
utilizar un lenguaje particular.
El lenguaje que entiende una computadora se denomina binario, pero como éste
es difícil de leer y escribir para nosotros, debemos usar un lenguaje intermedio que
luego será traducido a binario. El lenguaje, entonces, es una herramienta que nos
servirá para indicarle a la computadora qué pasos debe seguir para resolver el problema
en cuestión. Ahora bien, el modo en que especifiquemos la solución dependerá
del paradigma de programación que usemos.
Dicho paradigma no es más que un modelo que representa un enfoque particular
para la construcción de sistemas. No hay uno mejor que otro, sino que cada uno
tiene ventajas y desventajas. Por otro lado, hay situaciones en que un paradigma resulta
más adecuado que otro. El paradigma que vamos a desarrollar en el presente
libro es el de Programación Orientada a Objetos (POO).