Antonio Damigo

Programación Orientada a Objetos (POO)

por 30 de enero de 2013, 1614 visitas
La Programación Orientada a Objetos (POO) supuso todo un paradigma de la programación en los años 80, un enfoque distinto, un nuevo concepto, una nueva visión y análisis. Aunque ciertas dificultades encontradas que repercuten de manera directa en el desarrollo de la aplicación han llevado a su revisión y a nuevos diseños, desembocando en la Programación Orientada a Aspectos (POA).


Diseño orientado a objetos



La programación orientada a objetos es un paradigma de la programación que surge en los años 80 para superar las dificultades que presentaba el uso de los lenguajes imperativos procedurales.

En la programación procedural los datos eran independientes unos de otros, y era responsabilidad exclusiva del diseñador o programador, nunca del lenguaje, asociarlos a un mismo objeto. Cuando coexistían varios objetos con datos semejantes, se debían tener en cuenta no repetir nombres de variables de memoria para no causar errores graves en el programa.

La programación procedural estructurada tiene como elemento fundamental el procedimiento, la acción. Su modularización está encaminada a un desglose del problema en apartados cada vez más especializados en la realización de tareas concretas. Se identifica con el verbo.

En la programación OOP la unidad es el objeto. El objeto no es sólo un procedimiento, es algo más amplio, es un conjunto de datos y los procedimientos que pueden ser aplicados a esos datos. Se identifica con el nombre. Este conjunto de datos y procedimientos forman una unidad, un paquete, algunos lenguajes como ADA los denominan paquetes (package), y son tratados como una sola entidad, como si estuviesen en una cápsula. Esta propiedad de agrupar o encapsular los datos y los procedimientos relacionados con ellos, es lo que se conoce como encapsulación.

La encapsulación posibilita la capacidad de impedir el acceso a sus miembros desde exterior, o sea, la ocultación de datos. La encapsulación y la ocultación de datos son dos características fundamentales de los objetos. Las propiedades de los objetos, sus datos, tienen de esta forma controlado el acceso a su manipulación.

Este tipo de diseño se basa fundamentalmente en cuatro conceptos:

1) La definición de un tipo abstracto de datos: la Clase, los objetos y encapsulamiento.

2) Sobrecarga: de operadores y métodos.

3) Herencia: derivación de clases.

4) Polimorfismo.


1) Tipos abstractos de datos

Este nuevo concepto, el objeto, nos plantea toda una problemática en su tratamiento.

A los tipos de datos que incorporan los lenguajes se les denomina nativos o básicos. Estos tipos básicos tienen unas reglas propias de comportamiento que están implementadas en el compilador, por ejemplo, existe en Java un tipo int (entero) que tiene ya definida ciertas operaciones que pueden hacerse con elementos pertenecientes al mismo, la suma, la resta, etc.

Los lenguajes OOP permiten la creación de tipos de datos definidos por el usuario, estos son conocidos como tipos abstractos de datos (TAD) y pueden definirse las reglas de su comportamiento.

Concepto de clase

¿Qué es un clase? Una clase es un concepto abstracto, es una plantilla, es un modelo por el que se regirán los objetos que se formen a partir de la misma. Las clases están compuestas de miembros: datos y acciones. La nomenclatura puede variar según el lenguaje OOP. No obstante, el significado viene a ser el mismo en casi todos. Las clases en Java están formadas por : atributos y métodos.

A los datos se los denomina atributos o propiedades y a las acciones se les llaman métodos.

La declaración de una clase es como la declaración es una plantilla, no se está creando ninguna variable y el compilador solo reserva memoria para la plantilla. Cuando creamos una variable (instancia) de ese tipo de datos, es decir, de esa clase, entonces estamos creando un objeto.

2) La Sobrecarga.

Los lenguajes OOP tienen características fuertemente acentuadas que les distinguen de la programación procedural y estructurada. Una de ellas, es la posibilidad de que el programador pueda cambiar el significado de ciertos elementos o símbolos. Es decir, que a la invocación de un mismo nombre o elemento, éste pueda responder de forma diferente según los parámetros que intervengan en la llamada. Esto es lo que se conoce como sobrecarga y está implementada en casi todos los lenguajes actuales como Smalltalk, Forth, Eiffel, etc.

La sobrecarga es un recurso interesante porque evita que los programadores tengan que usar una multiplicidad de nombres para acciones afines. Con la sobrecarga se da mayor legibilidad y claridad al código, ya que simplifica notablemente el uso de identificadores.

Además contribuye de una forma importante al enfoque que la mayoría de los OOP pretenden, la extensibilidad.

Los lenguajes tradicionales enfocaban el crecimiento de los programas haciéndolos cada vez más complejos a medida que los problemas planteados eran más complicados. La extensibilidad permite cambiar el modelo del lenguaje adaptándolo a un problema concreto.

En Java sólo se permite la sobrecarga de métodos y no la sobrecarga de operadores, como en C++.

Sobrecarga de métodos

Cuando existen métodos que tienen el mismo nombre pero que su código es diferente, se dice que tales métodos están sobrecargados o que son métodos sobrecargados. Los métodos sobrecargados tienen el mismo nombre, pero más de una declaración y más de una definición, tantas como existan.

void iniciafecha(void);
void iniciafecha(int x);
void iniciafecha(int x, int y, int z);

La creación de métodos sobrecargados no supone la existencia de métodos idénticos. Estos métodos coinciden en el nombre, pero no pueden ser exactamente iguales porque el compilador no sabría distinguirlos. Los métodos sobrecargados deben tener argumentos diferentes, o en el tipo o en el número, que es lo que hace distinguir al compilador cuál de ellos debe aplicar.

3) La herencia.

La creación de clases es una característica que fundamenta y potencia la OOP. El poder crear objetos que sean abstracciones del mundo real, con propiedades y reglas de comportamiento, hace que los lenguajes OOP adquieran un dimensión hasta estos momentos desconocida. Estas propiedades y reglas de comportamiento forman un conjunto, una cápsula, que es común a todos los objetos de esa clase. Las clases, además, permiten que el acceso a estos elementos sea totalmente controlado por ellas mismas.

Otro de los pilares básicos de la OOP es la sobrecarga, que impide que los programadores tengan que crear multitud de métodos con distintos nombres para realizar acciones similares, facilitando así la claridad y legibilidad del código.

Todas estas innovaciones no son suficientes para soportar todo el diseño de la programación orientada a objeto. Las clases y la sobrecarga en sí mismas no permiten desarrollar el paradigma de la OOP. Se necesitan mecanismos que sean capaces relacionar unas clases con otras, que sean lo suficientemente potentes como para permitir que el código escrito para un proyecto pueda servir para otro sin tener costos excesivos en tiempo y seguridad. Con estas necesidades surge la herencia.

La herencia es un mecanismo que permite crear clases a partir de otras clases ya existentes, o lo que es igual, que unas clases se deriven de otras incorporando características de las clases madres o bases. Este mecanismo da lugar a una jerarquía de clases, donde junto a los elementos de las clases hijas o derivadas se incorporan los elementos de la clase padre. Tiende a imitar la transmisión natural, en la que los hijos, a sus características propias, incorporan las de sus padres mediante la herencia del código genético.

Ventajas de la herencia.

Esta propiedad hace que nuestros programas puedan desarrollarse de una forma más cómoda y apropiada a las nuevas necesidades. Cuando nuestros programas crecen y necesitan adaptarse a situaciones parecidas a las ya programadas, con una programación tradicional, debemos readaptar nuestro enfoque a esas nuevas situaciones, pero la herencia nos permite que no tengamos que readaptar el enfoque, sólo ciertas implementaciones, haciendo que un mismo interfaz pueda responder de forma diferente a distintas utilizaciones, o sea, que el lenguaje se adapta a situaciones concretas de programación y no es la programación la que debe adaptarse al lenguaje a medida que crece en complejidad. Esta propiedad de la herencia es conocida como la extensibilidad.

La extensibilidad nos ofrece la posibilidad de que nuestro programas tengan una misma interfaz y distinta implementación, según se necesite. Esto es debido a que podemos declarar clases de la cuales derivarán otras, y estas clases derivadas pueden implementar de forma distinta las propiedades heredadas.

Otro concepto nuevo y una de las más apreciadas ventajas de la herencia, es la reusabilidad o reutilización. Este nuevo concepto permite utilizar el código existente para nuevas extensiones de nuestros programas o para nuevos programas. El código escrito en las clases es reutilizado cuando se crean nuevas clases, a partir de las ya existentes, que lo heredan.

El reutilizar código es una gran ventaja, porque da seguridad, es utilizado el código probado, ya fiable. Porque ahorra tiempo, no hay necesidad de reescribir lo que ya tenemos escrito. Porque planifica nuestro trabajo, nos obliga a organizar nuestras clases para poder ser reutilizadas, y como consecuencia de esto último, da lugar a la creación de bibliotecas de clases que nos facilitarán nuestra programación.

La reusabilidad del código y la extensibilidad son dos pilares de la programación OOP



4) El polimorfismo.

Los conceptos fundamentales que sostienen a la programación orientada a objeto, clases, herencia, etc, mantienen una relación muy estrecha entre sí. El concepto de polimorfismo está íntimamente ligado a la herencia y a las clases.

La capacidad de mediante un mismo mensaje obtener respuestas diferentes, o métodos que con el mismo nombre realizan tareas diferentes, es conocida ya en la sobrecarga de métodos. Cuando a esta capacidad se le agregan algunas variaciones especiales, tenemos lo que se conoce como polimorfismo. Objetos de distintas clases responden a un mismo mensaje de forma también distinta.

Tenemos dos clases derivadas metal y nometal de otra llamada elemento, ambas clases incorporan el método combinación para obtener la fórmula del producto formado por la combinación de dos objetos. Cuando mandamos el mensaje combinación deseamos que se responda de forma diferente según los elementos a combinar, es decir, que el método no devuelva siempre la misma fórmula; sino que teniendo en cuenta los elementos decida que fórmula enviar.

Antes de entrar a tratar directamente el polimorfismo, hemos de conocer algunos conceptos previos imprescindibles: los tipos de ligaduras. Estos no son conceptos nuevos en programación. Como decíamos al principio, en los años cincuenta el lenguaje Lips ya incorporaba ligadura tardía.

Ligadura estática y ligadura dinámica

Cuando se crea un código mediante un lenguaje de alto nivel, en los procesos de compilación y enlace se deciden las posiciones que ocuparán en la memoria las llamadas a los métodos. Esta decisión se toma en tiempo de compilación y enlace, o sea, cuando se está generando el código ejecutable, antes de correr el programa. Esto es conocido como enlace anticipado o ligadura temprana o estática, (early binding).

Algunos lenguajes tienen una lista interna de métodos con sus direcciones y cuando se produce una llamada el compilador inserta la dirección de este método en el lugar de llamada. Este tipo de ligadura tiene sus ventajas, entre las que cabe destacar la rapidez de ejecución, ya que sólo se necesita el tiempo de paso de argumentos y almacenamiento en la pila de los parámetros a restaurar.

También presenta graves problemas, exige conocer de antemano todos los objetos que se usarán en todas las llamadas a los métodos. En algunas ocasiones esto es posible, pero existen otras en que no es posible saber a qué objeto llamar hasta el momento de la ejecución.

En el enlace tardío o ligadura dinámica (late binding), la posición no se define en tiempo de compilación, sino en tiempo de ejecución. Cuando se ejecuta el programa, en función de las variables, se determina el enlace entre los objetos.

La ventaja de este tipo de enlace es la flexibilidad, podrá decidirse entre un método u otro según las necesidades del programa, lo que nos lleva a manejar las jerarquías de clases de forma sencilla. El inconveniente es, sin duda, la eficiencia, ya que el tiempo de ejecución se alarga al tener que decidir que función debe invocarse.

En Java al igual que en otros lenguajes OOP como Eiffel y Smalltalk, existe la ligadura dinámica.

Este tipo de ligadura es propio de los lenguajes OOP, y aunque existen algunos lenguajes que aceptan los dos tipos, en los más ortodoxos sólo existe la ligadura dinámica.

Concepto de polimorfismo

Un objeto de la clase derivada es también un objeto de la clase base o clase madre. Esta dualidad es una ventaja en la que se apoya el polimorfismo. Pero ¿ qué es realmente el polimorfismo ?

El polimorfismo consiste en aprovechar las ventajas de la herencia de forma que cuando se llame a un método en una jerarquía de clases, el núcleo llama a la versión correcta del método basándose en el tipo estático de una variable.

Sólo tenemos que preocuparnos de llamar al método y no de la clase en la que está definido, el núcleo sabe que definición del mismo aplicar.



En resumen podemos decir que este tipo de diseño (DOO) resolvió muchas cuestiones que antes requerían un gran esfuerzo de una forma elegante, pero a pesar del avance que supuso, en la práctica surgieron una serie de dificultades que hicieron girar la tendencia hacia un nuevo y mejorado diseño:

La Programación Orientada a Aspectos.

Pero este será nuestro siguiente tema.

por Antonio Damigo.


Añadir comentario