CAPITULO I INTRODUCCION
I.1 El Paradigma de la Orientación a Objetos
El tema de la orientación a objetos no es nuevo. Sin embargo, en los últimos años ha tenido un impacto tan grande en diferentes áreas de la computación que se hace obligatorio el manejo de los conceptos involucrados para cualquier persona relacionada con esta disciplina. De la misma forma ha surgido una proliferación de definiciones e interpretaciones que conducen, primero a confusión, y luego a malas interpretaciones y abusos de los términos. En este primer capítulo se presenta una breve reseña de algunos acontecimientos que han reforzado las bases de este paradigma a través del tiempo, así como una exposición de la mayor parte de los términos que giran alrededor de esta filosofía.
Cuando Fortran apareció en 1957 como una forma de automatizar la labor de aquellos que programaban en lenguaje máquina y en ensamblador, hubo mucho escepticismo. Había que probar que un proceso automático de traducción podía competir con la eficiencia y el ingenio que aplicaban aquellos programadores para poder ahorrar un microsegundo o una unidad de almacenamiento; y así fue.
Este primer paso es sólo un ejemplo de la aplicación de un concepto clave en cualquier desarrollo computacional: abstracción. Ese concepto se sigue aplicando y es posible rastrearlo a lo largo de la historia de los lenguajes de programación.
Los lenguajes de la primera generación como Fortran I y Algol 58 estaban orientados a facilitar la manipulación de expresiones matemáticas y liberar a los programadores de los incómodos detalles que involucra (y más en ese entonces) el lidiar con la máquina en forma directa. Aunque manejaban el concepto de subprograma, este era aplicado más que todo para reutilización. Además, los programas eran relativamente planos, en el sentido de que consistían de una serie de subprogramas que referenciaban datos globales. Es fácil deducir que en programas de no necesariamente gran complejidad, empezarán a darse problemas serios si un sólo subprograma no funciona correctamente.
Para 1960, la popularización del transistor y la tecnología de circuitos integrados hicieron posible que los costos del computador disminuyeran y al mismo tiempo fuera posible aumentar su capacidad. Surgieron entonces lenguajes como Cobol y Algol 60, enmarcados dentro de la segunda generación. A diferencia de sus antecesores, éstos empezaron a dar mayor importancia al concepto de subprograma como un medio para realizar funciones abstractas. Es decir, al afrontar un problema se idealizaba un conjunto de acciones macro para solucionarlo, y estas a su vez serían atacadas como nuevos problemas, hasta llegar a completar la solución final. Lo anterior condujo al añidamiento de subprogramas, y al desarrollo de teorías referentes a estructuras de control, y a alcance y visibilidad de variables. Es decir, se construyeron las bases de la programación estructurada, aplicando una abstracción de los procedimientos requeridos.
Posteriormente, en la tercera generación se incluyó el concepto de módulo, como una forma de satisfacer la necesidad de manejar partes independientes de la solución que eran atacadas por diferentes personas, dado que los programas eran cada vez más complejos y más voluminosos.
Sin embargo, aunque ya se había trabajado bastante en la abstracción al nivel de módulos y de procedimientos, no se había hecho mucho en lo que se refería a la abstracción al nivel de datos. Aunque lenguajes como Cobol daban mucha importancia a los datos dentro de las aplicaciones, existía una separación clara entre datos y procedimientos. La solución era trazada con base en funciones, no en los datos mismos.
Incluso, al aplicar una metodología clásica de análisis y diseño estructurado, es posible tomar dos enfoques: 1) realizar primero el estudio y la especificación de las funciones requeridas y 2) luego formular el diagrama de datos, o al contrario, iniciar primero con el diseño del diagrama de los datos que se requerirán y las relaciones entre ellos, para luego deducir cuáles son los procesos necesarios que permitirán que los datos se mantengan actualizados y evolucionen para brindar la información última que se desee en el sistema.
Según Shankar, el punto es que la construcción de procedimientos funciona bien en la conceptualización de funciones abstractas, pero no necesariamente en la conceptualización de datos abstractos, y dado que la complejidad de los datos contribuye substancialmente a la complejidad del problema, la abstracción de procedimientos podría traer serios inconvenientes. Afirmaciones como la anterior llevaron al desarrollo de métodos de diseño basados en los datos, y a teorías referentes al concepto de tipo, que eventualmente se reflejaron en lenguajes como Pascal.
Estas ideas se vieron plasmadas por primera vez en el lenguaje Simula, un lenguaje para programar simulaciones en el computador en donde los objetos de una simulación eran modelados en forma directa como objetos de software. Simula es el antecesor de lenguajes como Smalltalk, Object Pascal, C++, Clos, y Ada, que se puede decir que son de desarrollo reciente. Estos lenguajes son llamados orientados a objetos. En ellos, un módulo es una colección de objetos, que son cápsulas que contemplan tanto un comportamiento (función) como un estado (datos), conformando una unidad donde los datos son el fundamento y estos pueden realizar "por sí mismos" un conjunto de acciones determinadas, tratando de simular el comportamiento de los objetos del mundo real (se entrará en detalles más adelante).
Las metodologías de
análisis y diseño orientadas a objetos tratan de establecer
normas para definir esas cápsulas y las relaciones con las demás
de forma que se maximicen una serie de criterios deseables previamente
establecidos para lograr un producto de calidad.
I.3 Elementos de
un modelo de objetos
Según Booch, el modelo de objetos está basado en cuatro elementos principales:
* Abstracción
* Encapsulamiento
* Modularidad
* Jerarquía
Es importante entender esos elementos antes de entrar al estudio de términos más específicos, pues son ellos los que determinan las características comunes de cualquier aplicación que se denomine "orientada a objetos".
Existen muchas definiciones de este término que pueden ayudar a comprenderlo. Según Hoare: "una abstracción surge del reconocimiento de similitudes entre diferentes objetos, situaciones, o procesos en el mundo real, y de la decisión de concentrarse en esas similitudes e ignorar por un momento las diferencias".
Shaw define el concepto como "una descripción simplificada de un sistema, que enfatiza algunos de los detalles o propiedades del mismo, pero que también omite otros. Una buena abstracción es una que enfatiza detalles que son significativos al lector y que suprime otros detalles que son, por el momento, irrelevantes".
Berzins, Gray, y Naumann apuntan que "un concepto califica como abstracción sólo si puede ser descrito, comprendido, y analizado independientemente del mecanismo que será utilizado eventualmente para realizarlo".
Booch concluye que "una abstracción denota las características esenciales de un objeto que lo distinguen de todas las otras clases de objetos, y entonces provee límites conceptuales claramente definidos, relativos a la perspectiva del observador".
Cada una de las definiciones anteriores coincide en el hecho de que una abstracción es un medio que permite un mejor manejo de la complejidad del problema, al permitir suprimir o posponer detalles irrelevantes en cierto momento, y concentrarse más bien, en detalles verdaderamente esenciales. El reconocer similitudes entre diferentes objetos también permite simplificar el dominio en estudio pues hace posible enunciar comportamientos o características generales que los describen, sin importar detalles que podrían no ser importantes.
Las abstracciones pueden ser de muchos tipos. Por ejemplo, pueden describir objetos que constituyen un modelo útil dentro del dominio del problema, u objetos que se conciben para agrupar acciones necesarias en la solución (objetos virtuales que son necesarios para controlar la interacción de los objetos entre sí).
Una abstracción debe tener límites conceptuales bien definidos que permitan la independencia funcional entre unas y otras. Así mismo, esos límites deberán esclarecer la forma en que la abstracción será utilizada para resolver y modelar el problema en cuestión. Es decir, al abstraer un objeto, debe quedar claro cuál es su función y su relación con los demás objetos.
Una abstracción es algo que no necesariamente existe en el dominio real de la aplicación pero que su existencia resultaría tan útil para el analista que termina siendo representada e incorporada en la solución computacional al problema.
Además, como apuntan Berzins y demás, debe ser independiente de la forma en que finalmente se lleve a cabo. Por ejemplo, la abstracción de una lista considera tanto las acciones que se permitirán realizar sobre ella, así como ciertas variables que mantienen el estado de la misma, pero no especifica que una lista debe ser realizada como un arreglo o mediante el uso de punteros. Esos son detalles que no vale la pena mencionar mientras se está introduciendo el concepto, pero que sí serán importantes en algún otro momento.
Encapsulamiento es una palabra que tiende a confundirse con mucha frecuencia debido a que se le asocian diferentes significados. Formalmente, encapsulamiento se refiere al fortalecimiento de las barreras de abstracción mencionadas anteriormente. Una abstracción debe poderse manipular como una sola unidad independiente que cumple con ciertas funciones. Cada abstracción debe ser claramente distinguible de las demás, así como la forma en que interactuarán entre ellas. Encapsular significa agrupar y manejar el grupo resultante como tal, y no cada parte a su vez.
Sin embargo, un concepto que muchas veces se señala como equivalente es el de ocultamiento de la información, el cual está más relacionado con el control del acceso a las partes de una abstracción. Es decir, está relacionado con protección.
Al decidir encapsular ciertas funciones o atributos, estos serán utilizados por otros objetos para llevar a cabo alguna tarea. Sin embargo, un objeto debe conocer de otro lo indispensable para que lo pueda utilizar, ni más ni menos. Si se conoce más, entonces aumenta la cantidad de información que se maneja lo que incrementa la complejidad, además de que hace más vulnerable el objeto que se utiliza, y es más difícil garantizar su integridad y su correcto funcionamiento. Por otro lado, si se esconden aspectos fundamentales, se estaría perdiendo el sentido de la abstracción.
En particular, la implementación de un objeto debe ser escondida dentro de la abstracción y no debe ser conocida por los objetos que lo utilicen (clientes). Según Ingalls, "ninguna parte de un sistema complejo debería depender de los detalles internos de otras partes".
La abstracción es un concepto externo al objeto que determina la forma en que es visto por los demás. El ocultamiento de la información es un concepto interno que determina qué cosas deben ser vistas por los demás. Es decir, son conceptos complementarios.
Se ha dicho que la complejidad de un programa crece en forma exponencial con respecto a su tamaño. Analizando un poco más las propiedades de una función exponencial es fácil deducir que si se tiene un programa grande, su complejidad será mucho mayor que la suma de la complejidad de varios programas más pequeños que cumplan el mismo objetivo. Si se pudiera tomar un programa grande y dividirlo en subprogramas según ciertos criterios, la complejidad resultante sería mucho menor. Sin embargo, no vale la pena particionar programas pequeños, y en programas grandes, existe un número a partir del cual las ventajas se tornan en lo contrario pues es tal la cantidad de módulos que puede volverse inmanejable.
Según Myers, "El acto de particionar un programa en componentes individuales puede reducir la complejidad en algún grado...Aunque particionar un programa es útil por esa razón, una razón más poderosa para particionar un programa es que crea un número de límites bien definidos y documentados dentro del programa. Estos límites, o interfases, son invaluables en la comprensión del programa.".
Las abstracciones, como se
mencionó anteriormente, son encapsuladas y protegidas para manejarse
como una única unidad lógica que se espera funciona correctamente.
Esas abstracciones son agrupadas a su vez para formar módulos, que
constituyen las unidades físicas de particionamiento de los programas.
Se espera que cada módulo pueda ser desarrollado con cierta independencia de los demás, y que incluso sea compilado en forma separada. Con esto se busca lograr una mayor productividad al desarrollar software en gran escala, en donde los módulos son asignados a las personas que componen el equipo de trabajo y los desarrollan en forma independiente y paralela. Cada módulo debe estar bien definido, y su acoplamiento con los demás debería ser el mínimo posible. Entre menos dependencias existan entre ellos, más mantenible será la aplicación final.
Lograr la modularización adecuada para un sistema no es un trabajo sencillo. Intuitivamente, se puede decir que se deben agrupar las abstracciones de acuerdo a su grado de relación lógica según el criterio del diseñador, y un módulo debe exponer sólo aquellas partes indispensables que los demás necesitan conocer. En ese sentido se dice que los módulos deben ser altamente cohesivos y poco acoplados entre sí.
Según Britton y Parnas: "El objetivo general de la descomposición en módulos es la reducción del costo del software al permitir que los módulos sean diseñados y revisados independientemente... Cada estructura de un módulo debería ser lo suficientemente simple como para que sea comprendida por completo; debería ser posible cambiar la implementación de un módulo sin tener conocimiento de la implementación de otros módulos y sin afectar el comportamiento de los mismos...".
Los módulos y el encapsulamiento ayudan a manejar el conjunto de las abstracciones identificadas, sin embargo, resulta muy útil y al mismo tiempo natural, clasificar u organizar las abstracciones de acuerdo a su naturaleza. El ser humano siempre tiende a clasificar los objetos que lo rodean, creando clasificaciones de especies, de elementos químicos, de materiales, etc. Esas clasificaciones forman jerarquías que permiten concentrarse en subconjuntos de elementos lo que facilita el estudio de los elementos como un todo.
El ejemplo más claro de jerarquía en el modelo de objetos es el concepto de herencia. Por ejemplo, los elefantes, tigres, vacas, y caballos son todos mamíferos de sangre caliente. Existe una clase de animales denominados mamíferos que comparten un conjunto determinado de características. Sin embargo, hay clases de animales, como las aves, que son de sangre caliente pero no son mamíferos. O sea, la clase de animales de sangre caliente contempla tanto los mamíferos como las aves. Todos los mamíferos y todas las aves heredan las características de los animales de sangre caliente, pero pertenecen a clases diferentes de animales.
Básicamente, la herencia define una relación entre las clases, donde una clase comparte la estructura y el comportamiento definida en una o más clases consideradas como superiores o superclases. La herencia define entonces una jerarquía de abstracciones, en donde una subclase hereda de una superclase, y además puede agregar o redefinir elementos a la estructura o al comportamiento previo.
Como señala Cox: “sin herencia cada clase sería una unidad independiente por sí misma, desarrollada a partir de la nada.” Las clases podrían no tener relación la una con la otra aún cuando fueran muy similares. La herencia hace posible definir software nuevo de la misma manera en que se introduce un concepto a un persona que lo desconoce, o sea, comparándolo con algo que ya existe y con lo que ya está familiarizado.
I.4 Conceptos básicos
El concepto de objeto puede ilustrarse a partir de muchos enfoques diferentes. Todos ellos parten de que un objeto representa una abstracción, pero la forma en que esta se caracteriza puede variar de un enfoque a otro. Por ejemplo, Snyder señala que: “la abstracción representada por un objeto se caracteriza por un conjunto de servicios que los clientes pueden solicitar”. Un cliente puede ser una persona o un programa, y un servicio puede ser una actividad que se realiza a partir de una solicitud del cliente. Define un objeto como cualquier entidad que juega un rol visible al proveer servicios a clientes.
Es decir, basa el concepto de objeto en la relación existente entre clientes y proveedores de servicios. La naturaleza de un cliente o de un proveedor depende del sistema que se esté analizando, pero lo importante es idealizar los servicios que se necesitan para realizar una labor y construir una solución basada en la interacción entre objetos que proveen servicios unos a los otros.
Aunque un objeto puede involucrar datos, un objeto no es solo eso. El propósito de los datos es representar información asociada con la abstracción que se realizó. Sin embargo, como se mencionó anteriormente, la abstracción puede incluir un conjunto de actividades o servicios que se necesitan. Estos servicios, al ser solicitados por un cliente, modifican los datos asociados a la abstracción para cumplir con una necesidad o un cambio que se de en el sistema.
Un objeto se ve entonces como una cápsula que contiene datos, y que además provee una serie de servicios que son utilizados por otros objetos para cumplir algún objetivo (ver figura 1). Los datos o variables que constituyen un objeto se conocen como atributos o componentes (los cuales podría ser a su vez objetos). Además, el estado de un objeto está determinado por el contenido de cada una de las variables que se asociarán al objeto. Cada uno de los servicios también se conoce como método u operación, y el conjunto de los métodos a los que puede responder el objeto se conoce como protocolo del objeto. *
Los clientes deben conocer los servicios que proveen otros objetos, pero no tienen porqué inmiscuirse con los detalles de cómo se llevará a cabo su solicitud. En lo que respecta al cliente, él solo sabe que envió una solicitud y que está llevándose a cabo. Además, supone que el servicio opera tal y como está estipulado; es decir, que funciona correctamente en el sentido de que hace lo que debe hacer.
El cliente debe conocer únicamente lo necesario del objeto que utilice, no más, y no menos. Esas operaciones secundarias también deberían ocultarse de alguna forma para que el cliente no pueda utilizarlas y ni siquiera tenga que conocer acerca de su existencia.
Una solicitud de un cliente identifica una operación. Esta operación denota el servicio que se ofrece, y puede tener parámetros asociados y resultados que retornar. Un parámetro o un valor retornado puede ser a su vez un objeto. En este punto surge la inquietud de cómo identificar cada uno de los objetos que se manipulan. Cada objeto debe poderse referenciar a través de un valor que permita identificarlo en forma directa y confiable. No debe ser necesario describir el objeto, sino solo especificar el valor que lo identifica.
"La identidad es aquella propiedad de un objeto que lo distingue de todos los otros objetos"
El mensaje es el mecanismo a través del cual los objetos pueden cooperar entre sí. Se dice que un mensaje es la invocación de un método de un objeto. Cuando un objeto A necesita de otro objeto B para cumplir con una operación propia, le envía un mensaje que resultará en la ejecución de las operaciones asociadas al método, y probablemente retornará algún resultado.
Esquema del concepto
de objetos.
En general las asociaciones entre los objetos se manejan a través de atributos. Es decir, uno de los atributos de A podría ser el objeto B, o una referencia a él.
Otro concepto es el de comportamiento: "el comportamiento es cómo un objeto actúa y reacciona, en términos de sus cambios de estado y envíos de mensajes" . En otras palabras, el comportamiento de un objeto se define completamente por sus acciones.
Los objetos pueden ser clasificados según la naturaleza de los servicios que brindan. La clasificación hace que los objetos que se manejan estén mejor organizados y sean más fáciles de entender y utilizar. Una clase es una descripción de un conjunto de objetos con características similares. A través de la clasificación se asocian características comunes a todos los miembros de una clase. Todos los objetos de una clase presentan la misma estructura y el mismo comportamiento. Además, se dice que un objeto es una instancia de una clase.
Idealmente, una clase es una implementación de un TDA (Tipo de Dato Abstracto). Esto significa que los detalles de implementación de la clase deben ser privados, de ella. Los datos son accesados a través de métodos (funciones de acceso), y también existen otras funciones para modificar la información (procedimientos de transformación).
Según Korson: "Herencia es una relación entre clases que permite la definición e implementación de una clase con base en la definiciones e implementaciones de otras clases existentes... Herencia es el concepto más promisorio en la consecución del objetivo de construir sistemas de software a partir de partes reutilizables, en vez de codificar manualmente a partir de bosquejos".
Finalmente, cabe destacar
que la estructura de clases se espera que sea fácilmente reutilizable
cuando sea necesario. Las clases pueden ser agrupadas para formar
bibliotecas, y al desarrollar un nuevo sistema cada vez se cuenta con una
mayor cantidad de ellas. La idea es tomar ese conjunto de clases
ya depuradas y construir a partir de ellas nuevas soluciones, modificando
la jerarquía de clases según se requiera.
Polimorfismo, como la misma palabra lo indica, significa múltiples formas. Existen muchos tipos de polimorfismo, pero en general una referencia polimórfica en un lenguaje orientado a objetos es una que atañe a instancias de más de una clase a través del tiempo. Así, una función polimórfica es aquella que puede ser aplicada uniformemente a cierta variedad de objetos. Por ejemplo, la misma función de suma puede utilizarse para representar la aplicación de esa operación a números enteros, números reales, matrices de números enteros, secuencias de caracteres, polinomios representados como listas, etc. También puede utilizarse el mismo operador para sumar un número a un vector.
En lenguajes orientados a objetos es posible definir la misma operación para diferentes clases. El operador "+" se define para cada una de ellas y eso permite realizar esa operación sobre elementos de diferentes clases, y combinaciones de ellos. Esta característica se conoce en algunos lenguajes como sobrecarga de operadores.
Recuérdese que mediante
herencia es posible redefinir funciones.
Finalmente, lo contrario al polimorfismo es el monomorfismo, que se encuentra en lenguajes fuertemente tipificados y de binding estático, como Ada.
I.5 Conceptos adicionales
Además de los temas analizados hasta el momento, es importante mencionar otros, que aunque no son un requisito para denominar a un producto "orientado a objetos" son puntos que están constantemente en discusión.
Muchos de los objetos en el mundo real son independientes, en el sentido de que cada uno puede realizar acciones sin necesidad de que exista un objeto superior que lo coordine. Si se tiene una aplicación meteorológica en donde existen varios sensores de temperatura, cada uno de ellos podría hacer lecturas en forma permanente y activar algún mecanismo de alarma si fuera necesario. Pero cada sensor es independiente y no existe un control directo sobre ellos.
El problema surge al hacer un diseño orientado objetos. Se identifica la clase sensor, y se tienen varias instancias de ella. Sin embargo, dependiendo de la plataforma de desarrollo que se posea, serán necesarios cambios fuertes.
Si se tiene un lenguaje o un sistema operativo que no provee ningún mecanismo de multiprogramación, o si se tiene una arquitectura de hardware con un único procesador, no es posible hacer una asociación directa, como se quisiera, entre los objetos del mundo real, y los objetos modelados en la aplicación.
Probablemente sería necesario introducir un objeto que estuviera activando permanentemente cada uno de los sensores para que realicen sus lecturas y hagan el reporte de alarma, o tal vez el nuevo objeto sea el que lo haga. Ese objeto, que se podría denominar un despachador (dispatcher) de eventos, en realidad no existe en el mundo real, pero como no es posible modelar los objetos en forma independiente, su existencia se justifica.
En el otro extremo, podría desarrollarse una arquitectura con varios procesadores donde en cada uno de ellos existe un sensor. O bien, cada uno de ellos sería un hilo (thread) de ejecución que aunque no sería simultáneo, puede compartir el tiempo de CPU con los demás bajo algún esquema de multiprogramación. Incluso, podrían mantenerse varias máquinas, cada una con un sensor, y unidas a través de una red.
Lim y Jhonson señalan que diseñar los aspectos de concurrencia en lenguajes orientados a objetos no es muy diferente de hacer lo mismo con otros lenguajes (según ellos, la concurrencia es ortogonal a la POO en los niveles de abstracción más bajos). Orientado a objetos o no, existen los mismos problemas.
Es decir, siempre es necesario lidiar con los problemas de compartir recursos (deadlocks, exclusión mutua, regiones críticas, starvation, sincronización de procesos, paso de mensajes, y otros). Sin embargo, todos estos detalles pueden relegarse a los niveles de abstracción bajos, y utilizar otras abstracciones que escondan esos detalles para hacerlos transparentes a los programadores.
El objeto, visto como una abstracción del mundo real, puede representarse como un thread de control (abstracción de proceso). Los objetos así representados son llamados objetos activos [Booch91].
Los objetos toman cierta cantidad de espacio, pero también de tiempo durante su existencia en la aplicación. Por ejemplo, las variables locales a un procedimiento existen mientras aquel se encuentre activo. Se crean en el momento de la invocación y al terminar el procedimiento son destruidas.
Las variables globales existen durante toda la ejecución de la aplicación y conservan su estado mientras esta esté activa. Sin embargo, ¿qué sucede si se necesita que algunos objetos sobrevivan a la aplicación que los crea?. Si en el lenguaje no se proveen mecanismos adecuados para manejar este problema, pueden surgir una serie de soluciones truculentas ideadas por los programadores, que tal vez no sean confiables, o necesiten revelar detalles de implementación propios del compilador, y de la forma en que conserva la información de los objetos. En lenguajes orientados a objetos, esta información no presenta una estructura simple, por aspectos que implica soportar herencia, polimorfismo y otras características de OO.
Según Booch: "Persistencia es la propiedad de un objeto a través de la cual su existencia trasciende el tiempo (i.e. el objeto continúa existiendo después de que su creador cesa de existir) y/o el espacio (i.e. la localización del objeto deja el espacio de direcciones en el que fue creado)" [Booch91].
La introducción del concepto de persistencia conduce a las bases de datos orientadas a objetos. Muchas de ellas siguen operando mediante los conceptos tradicionales de acceso a través de índices, modelo jerárquico, relacional, o de redes, pero presentan una interfaz al programador que le permite visualizar los objetos como tales. Las consultas se realizan a través de operaciones sobre objetos que trascienden la existencia de un programa individual.
El ofrecer persistencia es
algo más que guardar el estado de un objeto. Es necesario
almacenar la información que garantice que el estado del objeto
será interpretado de la misma forma por el programa, cuando vuelva
a ser ejecutado. Por lo tanto, la clase del objeto también
deberá trascender, lo cual no es fácil considerando que un
objeto puede cambiar de clase. El problema anterior sería
visto como un problema que atenta contra la integridad de la base de datos,
conforme esta crece y se torna más compleja.
I.5.3
Tipo
Según Deutsch, un tipo es una caracterización precisa de las propiedades estructurales y de comportamiento que comparten un conjunto de entidades. Para efectos prácticos, y en este texto, tipo y clase son conceptos equivalentes, aunque en algunos contextos no lo sea.
Booch agrega: "Tipo es el refuerzo de la clase de un objeto, de forma que los objetos de diferentes tipos no sean usados intercambiablemente, o a lo sumo, sean usados intercambiablemente bajo formas muy restringidos".
Un lenguaje de programación específico puede ser fuertemente tipificado, débilmente tipificado, o no tipificado.
Lenguajes como Object Pascal son fuertemente tipificados. Para cada variable se debe especificar claramente su tipo, así como para parámetros formales, y campos de clases. En tiempo de compilación se realizan todas las verificaciones de correctitud de tipos. En algunos casos puede ser necesario convertir o coersionar una variable de un tipo a una de otro para que la expresión sea válida para el compilador.
SmallTalk es un lenguaje no tipificado. Las variables se declaran sin tipo, y el significado de las operaciones no se determina sino hasta durante el tiempo de ejecución. Si se envía un mensaje no reconocido por un objeto, se generaría un error alusivo en tiempo de ejecución.
Tesler señala un conjunto de ventajas de los lenguajes fuertemente tipificados [Tesler81]:
* Sin chequeo de tipos, un programa puede convulsionar misteriosamente en tiempo de ejecución.
* En muchos sistemas, el ciclo edición-compilación-depuración es tan tedioso que una detección temprana de errores es indispensable.
* Las declaraciones de tipos documentan los programas.
* La mayoría de los compiladores pueden generar código más eficiente si se declaran los tipos.
En general, los lenguajes
no tipificados ofrecen mayor flexibilidad, pero los fuertemente tipificados
brindan seguridad.