2. Introduccion a la orientación a objetos

2. Introduccion a la orientación a objetos Dataprix 19 Octubre, 2009 - 10:22

Dado que PostgreSQL incluye extensiones de orientación a objetos (aunque no es, como ya hemos comentado, un SGBDOO completo), es interesante repasar algunos de los conceptos relacionados con este paradigma de programación y estructuración de datos.

2.1. El modelo orientado a objetos

2.1. El modelo orientado a objetos Dataprix 19 Octubre, 2009 - 10:29

En 1967, el lenguaje de programación Simula aplicaba algunas ideas para modelar aspectos de la realidad de forma mucho más directa que los métodos tradicionales. Desde entonces, la orientación a objetos (OO) ha adquirido cada vez mayor popularidad al demostrar sus ventajas, entre las cuales:

•    Permite un modelado más “natural” de la realidad.

•    Facilita la reutilización de componentes de software.

•    Ofrece mecanismos de abstracción para mantener controlable la construcción de sistemas complejos.

En el mercado aparecen constantemente herramientas y lenguajes de programación autodenominados orientados a objetos y los ya existentes evolucionan  rápidamente  incluyendo  nuevas  características  de  OO.  De  la  misma manera, se han desarrollado múltiples métodos y metodologías bajo este enfoque, cada una con aportaciones propias que llegan, en ocasiones, a resultar contradictorias entre sí. Se ha logrado la creación de un lenguaje unificado para el modelado, llamado precisamente UML (unified modeling language). La intención de UML es ser independiente de cualquier metodología y es precisamente esta independencia la que lo hace importante para la comunicación entre desarrolladores, ya que las metodologías son muchas y están en constante evolución.

Lamentablemente, a pesar de los muchos esfuerzos y de importantes avances, las ciencias de la computación no han creado aún una definición de modelo de objetos como tal. En un panorama como éste, es indispensable, al menos, la existencia de un modelo informal de objetos que oriente la evolución de la tecnología y que tenga la aprobación de los expertos en la materia. Un modelo así permitiría su estudio consistente por parte de los profesionales de las tecnologías de la información, facilitaría la creación de mejores lenguajes y herramientas  y,  lo  que  es  más  importante,  definiría  los  estándares  para  una metodología de desarrollo consistente y aplicable.

Sin embargo, este modelo no existe, lo que provoca inconsistencias incluso en el tratamiento de los principios y conceptos básicos de la OO. Por eso, es frecuente encontrar errores graves en el desarrollo de sistemas OO y, lo que es aún peor, se implementan soluciones de dudosa validez en herramientas de desarrollo que se dicen orientadas a objetos.

Aun sin haber alcanzado la madurez, la orientación a objetos es el paradigma que mejor permite solucionar los muchos y variados problemas que existen en el desarrollo de software. En los próximos apartados analizaremos los conceptos básicos de este modelo para identificar algunos de los problemas que aún debe resolver, lo que facilitará la comprensión y evaluación de métodos y herramientas OO.

2.2. Objetos: clase frente a instancia

2.2. Objetos: clase frente a instancia Dataprix 19 Octubre, 2009 - 10:56

Los objetos son abstracciones que realizamos del mundo que nos rodea y que identificamos por sus propiedades. Para la OO todo es un objeto.

Cada objeto tiene una existencia un tanto independiente de los demás objetos; es decir, tiene identidad propia. Aunque dos objetos tengan exactamente los mismos valores, no por eso serán el mismo objeto, seguirán siendo entidades diferentes. En los modelos OO, la identidad se representa con el identificador de objeto, IDO (OID en inglés, de object identifier). Teóricamente, el IDO de un objeto es único e irrepetible en el tiempo y el espacio.
 

Función del IDO                               
El IDO permite que dos objetos idénticos puedan diferenciarse, no es importante que el usuario conozca los IDO, lo importante es que los diferencie el sistema.

Los IDO son el mecanismo que permite hacer referencia a un objeto desde otro. De esta manera las referencias tejen las relaciones entre objetos.

Todos los objetos que comparten las mismas propiedades se dice que pertenecen a la misma clase. En los modelos OO, las clases le roban el papel central a los objetos, ya que es a través de ellas como se definen las propiedades de éstos y además se utilizan como plantillas para crear objetos.

 

 

Al crear un objeto utilizando la definición dada por una clase, obtenemos un valor para él, es lo que se llama una instancia del objeto. Durante la ejecución de los programas se trabaja con instancias. Como concepto, la instancia es equivalente a una tupla (fila) concreta en una tabla de una base de datos.

2.3. Propiedades: atributo frente a operacion

2.3. Propiedades: atributo frente a operacion Dataprix 19 Octubre, 2009 - 11:03

Las propiedades de los objetos pueden ser de dos tipos, dinámicas y estáticas. Un  atributo  representa  una  propiedad  estática  de  un  objeto  (color,  coste, edad, etc.). Una operación representa una propiedad dinámica; es decir, una transformación sobre un atributo o una acción que puede realizar.

El conjunto de valores de los atributos en un momento dado se conoce como estado del objeto. Los operadores actúan sobre el objeto cambiando su estado. La secuencia de estados por la que pasa un objeto al ejecutar operaciones definen su comportamiento.

La posibilidad de definir comportamientos complejos es lo que hace diferente la OO.

 

 

2.4. Encapsulamiento: implementacion frente a interfaz

2.4. Encapsulamiento: implementacion frente a interfaz Dataprix 19 Octubre, 2009 - 11:22

La estructura interna de los objetos debe estar oculta al usuario de un objeto, no necesita conocerla para interactuar con él. Los objetos se conciben como una cápsula cuyo interior está oculto y no puede ser alterado directamente desde el exterior.

Los tipos de datos abstractos           
Los tipos de datos abstractos (TDA) obedecen al mismo principio de independencia de la implementación. La diferencia respecto a los objetos es que éstos incluyen los datos y las peraciones en la misma cápsula.

A la estructura interna de un objeto se la denomina implementación y a la parte visible, la que se presenta al exterior, interfaz. La interfaz se define por sus atributos y operaciones.

La implementación de una operación se conoce como método. La implementación de un atributo se realiza generalmente con variables de instancia.

El encapsulamiento comporta las siguientes ventajas:

•    La modificación interna (de la implementación) de un objeto para corregirlo o mejorarlo no afecta a sus usuarios.

•    La dificultad inherente a la modificación de la implementación de un objeto sea independiente del tamaño total del sistema. Esto permite que los sistemas evolucionen con mayor facilidad.

•    La simplificación en el uso del objeto al ocultar los detalles de su funcionamiento y presentarlo en términos de sus propiedades. Al elevar el nivel de abstracción se disminuye el nivel de complejidad de un            sistema. Es posible modelar sistemas de mayor tamaño con menor esfuerzo.

•    Constituye un mecanismo de integridad. La dispersión de un fallo a través de todo el sistema es menor, puesto que al presentar una división entre interfaz e implementación, los fallos internos de un objeto              encuentran una barrera en el encapsulamiento antes de propagarse al resto del sistema.

•    Permite la sustitución de objetos con la misma interfaz y diferente implementación. Esto permite modelar sistemas de mayor tamaño con menor esfuerzo.

 

Estas características del encapsulamiento han contribuido en gran medida a la buena reputación de la OO.

Paradójicamente, el encapsulamiento, a pesar de ser uno de los conceptos básicos en la OO, no siempre se interpreta y se aplica correctamente. Especialmente en lo referente a encapsulamiento de atributos.

 

 

Diferenciemos operación y método a través de un ejemplo.

Consideremos tres objetos: polígono, círculo y punto.

A los tres se les solicita la operación de imprimir. En esta situación, tenemos que:

•  La operación solicitada es la misma, porque el significado del resultado es el mismo.

•  Cada objeto ejecuta la operación de forma diferente; es decir, con un método diferente.

•  Cada objeto, internamente, puede tener más de un método y selecciona el más apropiado según las circunstancias.

 

Las operaciones no son exclusivas de los tipos de objeto, los métodos sí. Una operación especifica “qué” hacer y un método “cómo” hacerlo. Esta diferencia permite tener múltiples métodos para una misma operación.

Veamos ahora la diferencia entre atributos y variables de instancia, que puede parecer más sutil.

Un atributo es la vista externa de una propiedad estática de un objeto. La representación interna puede variar, los atributos pueden implementarse también con métodos. Tomemos como ejemplo el objeto punto con los atributos que se muestran a continuación:
 

 

Punto
+ x: float
+ y: float
+ radio: float
+ ángulo: float
 

Los atributos de un punto pueden definirse en coordenadas angulares o rectangulares; en este caso, es posible conocer ambas representaciones. En la implementación de estos atributos, dos pueden ser variables de instancia y los otros dos se implementan como métodos, que se calculan a través de los primeros.

Desde el exterior no debe ser posible conocer la representación elegida internamente. Puede cambiarse la implementación de los atributos sin alterar la interfaz. En algunos casos puede incluso permitirse al sistema la elección de la representación interna de un atributo del mismo modo que una operación elige entre varios métodos disponibles.

2.4.1. Atributo frente a variable de instancia

2.4.1. Atributo frente a variable de instancia Dataprix 19 Octubre, 2009 - 11:40
Los atributos                                   
Un atributo puede ser almacenado en una variable o calculado por un método.

Un atributo especifica una cualidad de un objeto; una variable de instancia especifica cómo se almacenan los valores para esa cualidad.

Consideremos tres objetos, nombre, foto, vídeo, de los que necesitamos conocer el tamaño y prever, así, el espacio necesario para almacenarlos en disco.
En esta situación tenemos que:

  • El atributo es el mismo, porque su lectura tiene el mismo significado.
  • Cada objeto implementa el atributo de manera diferente. Sin importar la implementación, externamente todos los atributos entregan el mismo tipo de valor. Por ejemplo:
    –  El nombre puede utilizar un byte como variable de instancia, porque el tamaño de un nombre no puede ser mayor que 255 caracteres, o se puede implementar un método que calcule el tamaño en tiempo de ejecución.
    –  La foto utilizará dos o cuatro bytes.
    –  El vídeo puede almacenar el valor de tamaño en múltiplos de K.
  • Cada objeto puede tener implementaciones alternativas que se adapten a las circunstancias.

                      Variables de instancia vs. atributos

Lamentablemente, los lenguajes de programación comúnmente utilizados no implementan mecanismos adecuados para el encapsulamiento de los atributos, llegando, incluso, a permitir el acceso público a variables de instancia. A continuación, analizaremos las graves consecuencias de este hecho.

Acceder a un atributo es, en realidad, una operación que puede ser de lectura o de escritura. Por este motivo, frecuentemente se define el encapsulamiento como la ocultación de todas las variables permitiendo el acceso del exterior sólo para operaciones. Cuando el lenguaje de programación no ofrece independencia de la implementación en los atributos, se deben definir una variable de instancia y dos métodos por cada atributo: LeerAtributo y EscribirAtributo.

Las bases de datos relacionales tienen perfectamente diferenciada la interfaz de la implementación en sus tipos de datos: la forma de almacenarlos es completamente  independiente  de  la  forma  de  consultarlos  o  guardarlos.  No  se conciben las operaciones como internas a los objetos.

El  encapsulamiento  ha  sido  considerado  como  un  principio  central  de  la orientación a objetos y atentar contra él significa para muchos romper con sus reglas fundamentales. Sin embargo, las bases de datos orientadas a objetos tienen entre sus funciones la realización de consultas, que necesita acceder a los atributos de los objetos. Dado que los objetos se implementan con variables, al accederlos se rompe el encapsulamiento.

La mayoría de los lenguajes orientados a objetos permiten romper el encapsulamiento de forma parcial, declarando variables como públicas. El encapsulamiento, en estos casos, se proporciona como un mecanismo opcional, ya que el usuario puede declarar todas las variables públicas y, por lo tanto, accesibles directamente.

Otros lenguajes implementan operaciones de lectura/escritura que permiten acceder a las variables sin romper el encapsulamiento.

 

2.5. Herencia: jerarquia de clases

2.5. Herencia: jerarquia de clases Dataprix 19 Octubre, 2009 - 11:54

La herencia se define como el mecanismo mediante el cual se utiliza la definición de una clase llamada “padre”, para definir una nueva clase llamada “hija” que puede heredar sus atributos y operaciones.
A las clases “hijo” también se les conoce como subclases, y a las clases “padre” como superclases. La relación de herencia entre clases genera lo que se llama jerarquía de clases.

La herencia de tipo
En la herencia de tipo lo que hereda la subclase son los atributos de la superclase, pero no necesariamente su implementación, puesto que puede volver a implementarlos.

Hablamos de herencia de tipo cuando la subclase hereda la interfaz de una superclase; es decir, los atributos y las operaciones. Hablamos de herencia estructural cuando la subclase hereda la implementación de la superclase; es decir, las variables de instancia y los métodos.

La herencia de tipo define relaciones es-un entre clases, donde la clase “hijo” tiene todas las propiedades del “padre”, pero el “padre” no tiene todas las propiedades del “hijo”.

Consideremos una referencia mascota que es de tipo animal, en algún lenguaje de programación.

Ejemplo                                                 
Un gato es-un animal. Todas las propiedades de la clase “animal” las tiene la clase “ga to”. Pero un animal no-es necesariamente un gato. Todas las propiedades de gato no las tienen todos los animales.

mimascota: Animal;

Puede  hacer  referencia  a  objetos  de  tipo  animal,  o  tipos derivados  de  éste, como perro, gato o canario, por ejemplo.

mimascota = new Canario;

Se construye un nuevo canario y se hace referencia a él como mascota.

La propiedad de sustituir objetos que descienden del mismo padre se conoce como polimorfismo, y es un mecanismo muy importante de reutilización en la OO.

La referencia al tipo animal es una referencia polimorfa, ya que puede referirse a tipos derivados de animal. A través de una referencia polimorfa se pueden solicitar operaciones sin conocer el tipo exacto.

mimascota.comer();

La operación comer tiene el mismo significado para todos los animales. Como ya hemos comentado, cada uno utilizará un método distinto para ejecutar la operación.

Para conocer el tipo exacto del objeto en cuestión, se utiliza el operador de información de tipo. De este modo puede accederse a las propiedades específicas de un tipo de objeto que no están en los demás tipos.

En este ejemplo llamamos al operador información de tipo, instancia-de.
if (mimascota instancia-de Canario)
   mimascota.cantar();
Si la mascota es una instancia del tipo Canario entonces se le solicitará cantar, que es una propiedad que no tienen todas las mascotas.
 

Una clase puede heredar las propiedades de dos superclases mediante lo que se conoce como herencia múltiple.

En una herencia múltiple, puede ocurrir que en ambas superclases existan propiedades con los mismos nombres, situación  que  se  denomina colisión  de nombres. A continuación, se relacionan los posibles casos de colisión de nombres en la herencia de tipo:

  • Los nombres son iguales porque se refieren a la misma propiedad (ya hemos visto ejemplos de ello: la operación imprimir y el atributo tamaño). En este caso no hay conflicto porque el significado está claro: es la misma propiedad, sólo hay que definir una implementación adecuada.
  • Los nombres son iguales pero tienen significados diferentes. Esta situación es posible porque el modelado es una tarea subjetiva y se soluciona cambiando los nombres de las propiedades heredadas que tengan conflicto.

La herencia múltiple no comporta problemas para la herencia de tipo, puesto que no pretende la reutilización de código, sino el control conceptual de la complejidad de los sistemas mediante esquemas de clasificación.

Por lo que respecta a la herencia estructural, que, recordemos, consiste en que la subclase hereda las variables de instancia y los métodos de la superclase –es decir, la implementación–, la cosa cambia.

Para entender mejor la herencia estructural, diremos informalmente que representa una relación funciona-como. Por ejemplo, se puede utilizar para definir un avión tomando como superclase ave, de esta manera la capacidad de volar del ave queda implementada en el avión. Un avión no es-un ave, pero podemos decir que funciona-como ave.

Al aplicar la herencia de esta manera se dificulta la utilización del polimorfismo: aunque un objeto funcione internamente como otro, no se garantiza que externamente pueda tomar su lugar porque funciona-como.

El objetivo de la herencia estructural es la reutilización de código, aunque en algunos casos, como el ejemplo anterior, pueda hacer conceptualmente más complejos los sistemas.

Ejemplo                                                  
Si un canario es-un animal, entonces un canario funciona-como animal, más otras propiedades especificas de canario

Siempre que es posible aplicar la herencia de tipo, puede aplicarse la herencia estructural, por lo que la mayoría de los lenguajes de programación no hacen distinción entre los dos tipos de herencia.

 

Los lenguajes de programación comúnmente no hacen distinción entre la herencia estructural y la herencia de tipo.

La herencia estructural múltiple permite heredar variables y métodos de va rias superclases, pero surgen problemas que no son fáciles de resolver, especialmente con las variables de instancia.

Para resolver el conflicto de una variable de instancia duplicada, se puede optar por las siguientes soluciones:

  • Cambiar los nombres, lo que puede provocar conflictos en los métodos que las utilizan.
  • Eliminar una de las variables. Pero puede pasar que realicen alguna función independiente, en cuyo caso, sería un error eliminar una.
  • No permitir herencia múltiple cuando hay variables duplicadas.

Como se puede observar, no es fácil solucionar conflictos entre variables de instancia, por ello muchos lenguajes optan por diversos mecanismos incluyendo la prohibición de la herencia múltiple.

 

2.5.1. Tipo y clase

2.5.1. Tipo y clase Dataprix 19 Octubre, 2009 - 16:03

Tenemos que advertir que la mayoría de lenguajes de programación no diferencian los conceptos de tipo y clase y que la diferencia que establecen algunos autores no es demasiado clara. De todas maneras, la tendencia sería definir dichos conceptos como sigue:

•    Un tipo es un conjunto de objetos que comparten la misma interfaz.

•    Una clase es un conjunto de objetos que comparten la misma implementación.

Una solución que se aplica es incluir en el lenguaje el concepto de interfaz que define solamente las operaciones de una clase, pero no ofrece alternativas para los atributos. Sin embargo, con la diferenciación entre clases e interfaces no se logra la diferenciación entre los dos tipos de herencia, pues las clases se utilizan para representar relaciones es-un.

2.6. Agregación: jerarquía de objetos

2.6. Agregación: jerarquía de objetos Dataprix 19 Octubre, 2009 - 16:09
Ejemplo                                                  
Un automóvil está compuesto de carrocería, motor, ruedas, etc.

Los objetos son, por naturaleza, complejos; es decir, están compuestos de objetos más pequeños. Un sistema de información debe reflejar esta propiedad de los objetos de forma natural. En una base de datos relacional, un objeto complejo debe ser descompuesto en sus partes más simples para ser almacenado. Al extraerlo, es necesario ensamblar cada una de sus partes.

Por este motivo el modelo relacional comporta problemas cuando se utiliza en aplicaciones como el CAD, donde los objetos que se procesan son muy complejos.

Las bases de datos de objetos deben proporcionar la facilidad de obtener un objeto complejo en una sola consulta de forma transparente. En este caso, los apuntadores son un mecanismo excelente para representar composición, ya que permiten acceder rápidamente a las partes componentes de un objeto, sin importar su lugar de almacenamiento.

Las bases de datos requieren independencia de la aplicación, lo que provoca un conflicto conceptual cuando se trabaja con objetos compuestos: las bases de datos deben almacenar información independiente de la aplicación para que nuevas aplicaciones puedan hacer diferentes interpretaciones de la información original; pero con los objetos compuestos esto no es tan sencillo, puesto que suelen tener una sola interpretación, o mejor dicho, una sola manera de ser consultado en una base de datos.

2.7. Persistencia

2.7. Persistencia Dataprix 19 Octubre, 2009 - 16:14

La persistencia se define como la capacidad de un objeto para sobrevivir al tiempo de ejecución de un programa. Para implementarla, se utiliza el almacenamiento secundario.

Se han propuesto varios mecanismos para implementar la persistencia en los lenguajes de programación, entre los que podemos destacar los siguientes:

•    Archivos planos. Se crean archivos para almacenar los objetos en el formato deseado por el programador. Los objetos se cargan al abrir el programa y se guardan al finalizar. Ésta es la opción más accesible para   todos los lenguajes de programación.

•    Bases de datos relacionales. Los objetos son mapeados a tablas, un módulo del programa se encarga de hacer las transformaciones objeto-relacionales. Este enfoque consume mucho tiempo al realizar el mapeo. Existen algunas herramientas que realizan mapeos semiautomáticos.

•    Bases de objetos. Los objetos son almacenados de forma natural en una base de objetos, y la consulta y recuperación es administrada por el gestor, de esta forma las aplicaciones no necesitan saber nada sobre los detalles de implementación.

•    Persistencia transparente. Los objetos son almacenados y recuperados por el sistema cuando éste lo cree conveniente, sin que el usuario  deba hacer ninguna solicitud explícita de consulta, actualización o  recuperación de información a una base de objetos. No se requiere, por lo tanto, otro lenguaje para interactuar con las bases de datos.

2.8. PostgreSQL y la orientacion a objetos

2.8. PostgreSQL y la orientacion a objetos Dataprix 20 Octubre, 2009 - 10:14

El argumento a favor de las bases de datos objeto-relacionales sostiene que permite realizar una migración gradual de sistemas relacionales a los orientados a objetos y, en algunas circunstancias, coexistir ambos tipos de aplicaciones durante algún tiempo.

El problema de este enfoque es que no es fácil lograr la coexistencia de dos modelos de datos diferentes como son la orientación a objetos y el modelo relacional. Es necesario equilibrar de alguna manera los conceptos de uno y otro modelo sin que entren en conflicto.

Uno de los conceptos fundamentales en la orientación a objetos es el concepto de clase. Existen dos enfoques para asociar el concepto de clase con el modelo relacional:

1.er enfoque: las clases definen tipos de tablas
2.º enfoque: las clases definen tipos de columnas

Dado que en el modelo relacional las columnas están definidas por tipos de datos, lo más natural es hacer corresponder las columnas con las clases.

1.er enfoque 2.o enfoque
Los objetos son valores tuplas
Las clases son dominios tablas
Los mecanismos de extensión          
No es habitual que el usuario utilice los mecanismos de extensión pues se consideran mecanismos avanzados.

PostgreSQL implementa los objetos como tuplas y las clases como tablas. Aunque también es posible definir nuevos tipos de datos mediante los mecanismos de extensión.

Dado que las tablas son clases, pueden definirse como herencia de otras. Las tablas derivadas son polimorfas y heredan todos los atributos (columnas) de la tabla padre (incluida su clave primaria). Si no se manejan con precaución, las tablas polimorfas pueden conducir a errores de integridad al duplicar claves primarias.

Veremos estos conceptos más
en detalle en el subapartado 4.2 de esta unidad didáctica.

PostgreSQL soporta algunas extensiones del lenguaje SQL para crear y gestionar este tipo de tablas.