Documentos de Académico
Documentos de Profesional
Documentos de Cultura
Programación Orientada A Objetos
Programación Orientada A Objetos
orientada a objetos
y tcnicas avanzadas de
programacin
Carlos Fontela
2003
CARLOS FONTELA
-2 -
1. PRLOGO
Empec a escribir esta obra como tercer tomo del Curso de Programacin 1 , cuyos volmenes I y II
salieron en 19992 . Pero pronto se fue convirtiendo en un texto independiente de aqullos, por varias razones.
De todas maneras, es heredera de aquella obra en cuanto a que utiliza una metodologa similar.
Adems, presumo que el lector maneja la mayor parte de los conceptos enseados en la misma, como
programacin estructurada y modular, el lenguaje Pascal y preferiblemente tambin C.
Asimismo, tambin inclu temas que estn ligados a la forma en que enseo la materia Algoritmos y
Programacin III en la Facultad de Ingeniera de la Universidad de Buenos Aires.
Finalmente, la versin preliminar de esta obra apareci en dos partes, en septiembre y noviembre de
2002, respectivamente.
Este libro se pudo haber denominado Programacin Superior. Si bien el ttulo parece un poco
pretencioso, sirve como ltimo curso de programacin para personas que manejen el paradigma estructurado.
Encara, desde la ptica de la programacin orientada a objetos, una serie de problemas de programacin de
cierta complejidad que hacen que slo se puedan analizar una vez afianzados los conceptos bsicos de
programacin.
En este sentido, este no es un libro en el cual se ensee a programar empezando por el paradigma de
objetos. Ese es un desafo que pienso acometer alguna vez.
Por qu programacin orientada a objetos? Desde el comienzo de la programacin, todos los
lenguajes tratan de abstraer. Los ms antiguos abstraen haciendo un modelo de la mquina subyacente, como en
el caso extremo de Assembler. Algunos se basan en su particular visin del mundo, como Lisp, Prolog, APL,
Cobol y RPG. Los ms modernos intentar hacer un modelo del problema a resolver. La programacin orientada
a objetos es el caso ms reciente de intentarlo desde un propsito general, e intenta describir las soluciones en
trminos de cada problema. Pero este paradigma tie ne sentido si se trata de un desarrollo mediano: no tiene
sentido escribir un programa tipo hello, world 3 con objetos.
Esta obra no se queda slo en la programacin orientada a objetos. Analizaremos tambin
programacin por eventos, como aplicacin de los objetos y conceptos de programacin en interfaces de
usuario grficas. Asimismo, exploraremos los temas de persistencia, pruebas y documentacin, desde el punto
de vista de la programacin orientada a objetos, y veremos el uso de algunos de los diagramas que utiliza el
lenguaje unificado de modelado UML.
Mi experiencia enseando programacin orientada a objetos, con eventos e interfaz de usuario
grfica me mostr las diferencias y semejanzas que existen en los lenguajes que la implementan, y desde ese
punto de vista, me pareci interesante ejemplificar sobre tres de ellos. He mechado con ejemplos en los tres,
indistintamente, y se han explicado los distintos conceptos aplicndolos a los tres lenguajes.
Como lenguajes, hemos elegido los tres ms populares, aun sabiendo que provienen de un tronco
comn 4 : Object Pascal, C++ y Java. Y hemos explicado los conceptos utilizando los tres indistintamente, sobre
todo porque esto nos permite analizar diferentes soluciones y mostrar semnticas y funcionalidades que pueden
no ser caractersticas de todos los lenguajes.
Para un aprendizaje ms veloz, recomendamos a quienes manejen bien Pascal que vean primero las
implementaciones en Object Pascal. Quienes manejen bien C, podrn optar por Java, ms orientado a objetos, y
C++, ms parecido a C. De todas maneras, creo que es perfectamente posible leer las tres variantes y sera
importante para captar las diferentes visiones sobre cada tema.
1
Curso de Programacin, Carlos Fontela, Centro de Estudiantes de Ingeniera La Lnea Recta, Buenos Aires, 1999.
El tomo III haba quedado en una versin preliminar que finalmente abandon totalmente.
3
Es un clsico de los libros de programacin aunque no de los mos empezar la enseanza de un lenguaje con un programa que
emite un saludo que dice: Hello, World.
4
Todos son herederos de la programacin estructurada.
2
-3-
CARLOS FONTELA
Cuando en el libro decimos Java nos referimos a la versin 2 del lenguaje, C++ es el de la definicin
del estndar ISO C++ de 2000, que ampli considerablemente el C++ del libro de Stroustrup [4], y Object
Pascal es el utilizado en Borland Delphi 6 o Kylix 3.
Parafraseando a Humpty Dumpty 5 , cuando uso una palabra en el texto, significa exactamente lo que
quiero que signifique, ni ms ni menos. Creo que este es un principio fundamental cuando se ensean
conceptos. Esto me oblig a ser ms preciso al separar los conceptos de sobrecarga y redefinicin, que en obras
anteriores haban sido utilizados como trminos equivalentes, o los de definicin y declaracin.
La obra no fue pensada para ser utilizada como texto de referencia, por lo que contiene numerosas
vinculaciones entre sus temas, y se supone que se lee en forma secuencial.
Esta versin cuenta con unos cuantos ejercicios resueltos. Sin embargo, a las soluciones las he
separado del texto principal ponindolas en apndices, de modo que no entorpezcan el estudio de los conceptos
tericos.
Al final del libro se presenta la bibliografa con la que trabaj y que puede servir de consulta para los
lectores. En los casos de libros que estn publicados en la Web prefer citar las ediciones en papel, sobre todo
por la volatilidad de las direcciones electrnicas. En los casos en que slo cont con direcciones de la Web, las
cit de todos modos, con la esperanza de que si deciden mudarlas se puedan encontrar fcilmente con un
buscador el da de maana.
Quiero agradecer a todos los docentes auxiliares y alumnos que he tenido en la Facultad de
Ingeniera de la Universidad de Buenos Aires, en la Universidad Catlica Argentina y en otros mbitos, que me
han hecho llegar ideas y crticas respecto de las otras obras similares y de la forma de encarar los temas.
Especialmente quiero agradecer a Francisco Rey y Javier Macchi, de quienes tom el contenido de
gran parte del captulo de programacin por eventos, y a Pablo Surez, a quien le debo una porcin ms que
considerable del captulo de pruebas.
Le dedico la obra a mi mujer, Ana, y a mis hijos, Joaqun y Clara.
Desde ya, cualquier observacin o crtica que los lectores crean conveniente hacerme llegar, ser
bienvenida y ayudar a mejorar futuras ediciones.
Carlos Fontela
Buenos Aires, marzo de 2003
5
-4 -
NDICE DE CONTENIDOS
1.
Prlogo
2.
9
9
9
10
10
11
13
14
14
15
16
16
16
17
17
17
17
18
19
3.
21
21
21
22
22
22
23
23
23
23
24
24
24
24
25
25
25
26
26
26
26
28
28
30
32
34
34
-5-
CARLOS FONTELA
34
34
34
35
35
35
35
35
4.
36
36
36
37
37
37
39
40
41
41
41
42
42
42
42
43
45
45
45
46
47
49
49
49
49
49
50
50
50
50
50
5.
51
51
51
51
52
52
53
54
55
55
55
56
56
57
-6 -
6.
Transformacin de tipos
Moldeo automtico y moldeo explcito
Datos estructurados con elementos de varios tipos
Informacin de tipos en tiempo de ejecucin
Implementacin del polimorfismo en distintos lenguajes
Polimorfismo y otros aspectos en Object Pascal
Polimorfismo y otros aspectos en C++
Polimorfismo y otros aspectos en Java
Ejercicio resuelto: propiedades de una inmobiliaria
Enunciado
Solucin en Object Pascal
Solucin en C++
Solucin en Java
Ejercicio resuelto: uso de la implementacin de la inmobiliaria
Enunciado
Solucin en Object Pascal
Solucin en C++
Solucin en Java
57
57
58
59
60
60
61
62
63
63
63
63
63
63
63
63
63
63
64
64
64
64
66
70
71
72
75
75
75
75
77
78
78
79
82
82
83
-7-
CARLOS FONTELA
Parte I: Programacin
orientada a objetos
-8 -
-9-
CARLOS FONTELA
Programacin modular
La llamada programacin modular busc cubrir los baches que dejaba la simple aplicacin de la
programacin estructurada, sobre todo en el desarrollo de sistemas de tamao medio a grande, aunque buscaba
objetivos similares.
La idea consisti en dividir un programa en un conjunto de mdulos o subprogramas autnomos que
fueran individualmente programables, verificables y modificables. En muchos casos estos mdulos fueron
desarrollados por distintos programadores y eran testeados y mantenidos en forma independiente.
Cuando se elige desarrollar el sistema haciendo un refinamiento de un problema desde lo ms
complejo a lo ms elemental, el mismo se escribe como una secuencia de acciones no primitivas que luego son
desarrolladas como subprogramas. Esto se conoce como programacin "top-down" o descendente, el primer
estilo encarado en el marco de la programacin modular.
En otros casos los subprogramas sirven para escribir una serie de acciones no primitivas, de uso
relativamente frecuente, que luego se usan en varios programas, constituyendo lo que se conoce con el nombre
de biblioteca de subprogramas. Estas bibliotecas (en ingls "libraries"7 ) pueden ser tan amplias que los
programadores escriban programas en un lenguaje casi propio, que permite aun el manejo de ciertos tipos y
estructuras de datos que el lenguaje original no manejaba. A menudo esto es llamado programacin "bottomup" o ascendente, y pronto se convirti, no en un sucedneo de la programacin descendente, sino en su mejor
complemento.
Es tambin de gran utilidad la divisin de un programa en mdulos en la etapa de prueba, pues cada
subprograma puede ser probado en forma separada antes de integrarlos al sistema. Esto permite que en el
momento de la integracin del sistema se pueda tener la seguridad de que cada uno de los mdulos no posea
errores internos, restando solamente la prueba en conjunto para ver la interaccin entre los mismos.
El adecuado uso de mdulos permite tambin un mejor mantenimiento, ya que si se necesita
modificar alguna tarea de las que efecta el sistema, esto se podr hacer modificando el mdulo que realiza
dicha tarea, sin necesidad de tocar todo el programa.
Se enfatiza asimismo el uso de declaraciones locales, que confina identificadores a espacios de
nombres ms reducidos, y se desalienta el de identificadores globales, sobre todo cuando se trata de variables.
En esta misma lnea, se recomienda que las interacciones entre mdulos se limiten al pasaje de parmetros.
Otras recomendaciones apuntan a reducir el acoplamiento entre mdulos y aumentar la cohesin.
Esto es, garantizar que cada mdulo tenga la menor cantidad de interacciones posibles con los dems mdulos
(conexiones angostas), a la vez que cada uno debe hacer una sola cosa simple, trabajando sobre la misma
estructura de datos.
Las modificaciones en lenguajes de programacin que apuntaban a la programacin estructurada
tambin implementaron la programacin modular. As, Fortran 77, Algol 80, Pascal y C, manejaban mejor la
modularidad que sus predecesores.
El concepto de programacin modular tambin fue introducido en mi obra antes mencionada, en el
tomo I. Tambin en el tomo II se analiza el tema.
Abstraccin
Pero la programacin estructurada y modular, as como estaba encarada, ya no resolva todos los
problemas a mediados de la dcada de los 80. Por eso, muchos empezaron a cuestionar por qu la industria del
software no haca lo que otras industrias, que construyen sus productos en base a componentes desarrollados
7
- 10 -
por un tercero.
As, por ejemplo, una fbrica de automviles no pretende producir los equipos de audio o de aire
acondicionado, las bateras, las lamparitas o las cubiertas que incluye en sus modelos, y a veces ni siquiera los
motores. Por qu hacen esto? Precisamente porque comprrselos a quien produce estas autopartes en forma
masiva suele ser ms econmico, adems de que permite que cada uno se dedique a lo que mejor sabe hacer.
Lo mismo puede aplicarse al desarrollo de software, utilizando lo desarrollado por otros en nuestras
aplicaciones o incluso lo desarrollado en casa para otros sistemas. Esto se llama abstraccin.
De esta forma se puede ir construyendo un lenguaje de nivel superior. Por ejemplo, supongamos que
agregamos a Pascal una funcin de exponenciacin, que el lenguaje no tiene. Si incluimos esta funcin en una
unidad de biblioteca, podremos utilizarla luego en nuestras aplicaciones. Asimismo, si el mdulo a desarrollar
fuera ms complejo, podramos pensar en comprarlo a alguien que lo haya desarrollado.
La abstraccin se utiliza tambin para lo que se denomina implementacin de tipos de datos
abstractos (TDA). Diremos que trabajamos con un TDA cuando manejamos un tipo de dato que no est
predefinido en el lenguaje utilizado, simulndolo de manera totalmente transparente, mediante un adecuado uso
de una biblioteca de subprogramas. Esto permite manejar tipos de datos que el lenguaje no posee, ya testeados
en otros contextos.
Puesto que los tipos de datos no son conjuntos sino lgebras, es decir, conjuntos ms las operaciones
definidas sobre los elementos de los mismos, se hace necesario especificar ambas cosas. Por ello, la
implementacin de un TDA implica siempre las siguientes 3 tareas:
?? Determinacin de las operaciones necesarias sobre el TDA.
?? Eleccin de una estructura de datos sobre la cual se materializar la implementacin.
?? Implementacin de la estructura de datos y las operaciones.
Los lenguajes de programacin modulares no tenan en general un buen soporte para la abstraccin.
Slo Ada pareci reconocer esta necesidad y en algunas implementaciones de Pascal se introdujo el concepto
de unidad de biblioteca. C implement una funcionalidad incompleta.
A pesar de los aos que tiene el concepto, la abstraccin se ha utilizado en sus primeros tiempos casi
siempre dentro de una misma empresa o equipo de desarrollo, sin que se diera ese comercio de componentes de
software que se viene anunciando desde los 80. Sin embargo, desde hace un lustro aproximadamente, gracias a
la tecnologa de objetos y el auge de Internet, ha surgido este mercado que est comenzando a revolucionar el
desarrollo de software. Tal vez en unos aos ms los desarrolladores de software terminemos comprando la
mayor parte de lo que necesitemos, enchufando componentes y probndolos en conjunto.
La abstraccin de datos est analizada en el tomo II de mi obra ya citada.
Ocultamiento de implementacin
El problema del uso de la abstraccin, sobre todo si hablamos de TDA, consista en que no haba
forma de impedir que el usuario del mismo (un programador de aplicaciones que denominaremos cliente)
hiciera un uso indebido a los datos, porque tenda a utilizar conceptos propios de la implementacin que no
debieran ser de su incumbencia.
Volviendo a la analoga de la industria automotriz, quien provee a una fbrica de automviles de
equipos de audio no suele especificar cmo estn construidos los mismos. Solamente basta con que asegure una
serie de conectores o interfaces que permitan su instalacin y una serie de caractersticas y funcionalidades a
prestar. De esta manera, el fabricante del equipo de audio puede establecer cambios en los circuitos internos sin
necesidad de que el fabricante de autos deba modificar nada si se respetan las interfaces y las prestaciones
establecidas. Esto lo logra porque el equipo de audio es, desde el punto de vista de la fbrica de automviles,
una caja negra, que se testea cerrada, teniendo en cuenta solamente que cumpla las prestaciones establecidas
- 11 -
CARLOS FONTELA
por un contrato y que tenga las interfaces necesarias para poder conectarlo.
Cmo puede este concepto ser llevado a la industria del software? Con el principio del
ocultamiento de implementacin, tambin llamado ocultamiento de informacin u ocultamiento de datos.
Este concepto permite el desarrollo de software de modo tal que el cliente no pueda usar la estructura de datos y
los algoritmos subyacentes. En otras palabras, se pueden usar las interfaces de los TDA, pero no la
implementacin. De este modo, la interfaz se convierte en una especie de contrato entre las partes, que debe ser
respetado.
El ocultamiento de implementacin precisa un par de normas respecto de la especificacin 8 :
?? Al cliente, se le debe proveer slo toda la informacin que necesita para usar el mdulo
correctamente.
?? Al que implemente el mdulo, se le debe proveer toda la informacin necesaria para
comprender el uso que se har del mismo, pero ninguna informacin ms.
Por lo tanto, una especificacin debe sealar cules son las funciones que el mdulo realiza,
describiendo el dominio de cada una, los valores iniciales, los parmetros que se deben utilizar y el efecto de
dichas funciones.
El ocultamiento implica separar el qu del cmo. Es decir, el cliente debe conocer qu hace el
mdulo, pero no necesariamente cmo lo hace.
Todo esto puede parecer extrao: por qu ocultarle al usuario de la biblioteca lo que no necesita?
Las ventajas del ocultamiento son bsicamente dos:
?? Permitir cambios de implementacin.
?? Impedir violaciones de restricciones entre datos internos.
La primera ventaja se refiere a que, por no poder usar el cliente la estructura de los datos y
algoritmos, no va a utilizar propiedades exclusivas de stos, de modo que en futuras modificaciones se podr
alterar dicha estructura sin que sean necesarios cambios en los programas clientes.
Por ejemplo, si se desarrolla un TDA que maneja pilas de enteros trabajando sobre arreglos, y se le
da al cliente acceso a la impleme ntacin, ste puede utilizar facilidades propias de la forma de implementacin.
Si luego, por consideraciones diversas, se establece que conviene implementar las pilas sobre listas
encadenadas, no podr hacerse sin obligar al cliente a modificar sus programas, lo cual alterara el contrato
entre implementador y cliente.
Ntese, en consecuencia, que de no ocultar la implementacin, se estara impidiendo su evolucin.
La segunda ventaja que se mencion se refiere a las restricciones que pueda haber entre datos
internos, que hayan sido establecidas por el implementador como resguardo para el buen funcionamiento, y que
puedan ser arruinadas por el acceso indebido del cliente.
Por ejemplo, si el implementador establece tres datos de un TDA vinculados entre s, dejar librado el
acceso a la implementacin al cliente puede arruinar el trabajo hecho. Un ejemplo de esto puede ser un grafo no
dirigido implementado de forma tal que por cada arco que se agrega en un sentido se agrega automticamente
su simtrico, y cada vez que se elimina un arco se eliminan ambos. Un cliente que no entiende del todo la
implementacin puede intentar agregar o suprimir un arco sin preocuparse por la simetra.
De todas maneras, es importante destacar que el ocultamiento de implementacin debe ser cuidadoso
en respetar el contrato entre cliente e implementador en todo momento. Vale decir, las versiones futuras de un
mismo TDA o mdulo deben tener, por lo menos, la misma funcionalidad y la misma interfaz que la primera
versin, y no introducir efectos colaterales. Por lo tanto, podr agregar nuevas operaciones, pero debo
garantizar que las antiguas operaciones permanecen y mantienen la misma interfaz.
8
- 12 -
Asimismo, la implementacin debe respetar en todo momento las restricciones entre datos internos
que sean necesarias.
Adems, para que sea til al cliente, toda la funcionalidad esperable de un TDA debera estar
accesible en forma de subprogramas. Decimos en este caso que la implementacin es completa . Por eso suele
ocurrir que se le oculta al usuario la estructura interna de un TDA y las implementaciones de los mdulos, pero
se le deja ver el encabezado de estos ltimos.
En definitiva, se oculta la implementacin para que ningn cliente sea dependiente de una forma de
implementacin en particular. O dicho de otro modo, para que el cliente pueda estar seguro de que est usando
el TDA correctamente, y el implementador pueda introducir cambios y mejoras en el futuro.
El concepto de ocultamiento de implementacin slo se dio en algunos lenguajes, como Ada y
Modula -2, antes del surgimiento de la programacin orientada a objetos.
Hacia un nuevo paradigma
La gran pregunta que cabe hacerse es, despus de todo lo visto, por qu seguimos buscando nuevas
tcnicas de desarrollo?
El problema principal es que la crisis del software sigue, y probablemente no terminar nunca. Una
de las causas ms fuertes es la huida hacia adelante 9 que se verifica por el aumento de complejidad de los
sistemas. As, cuando una tcnica estaba resultando til, el enorme crecimiento de la complejidad la hace
obsoleta.
Esto es bastante lgico si lo pensamos humana y tecnolgicamente. Cuando las computadoras eran
muy caras y el costo de desarrollar software muy alto, los programas resolvan tareas elementales. A medida
que el costo del hardware baj, surgieron nuevos lenguajes de programacin y hubo tcnicas mejores de
desarrollo de software, se pudieron crear sistemas cada vez ms complejos, por lo que esos hardware, lenguajes
y tcnicas resultaron insuficientes. Con el tiempo el hardware sigui abaratndose, surgieron ambientes de
desarrollo amigables y se crearon metodologas de desarrollo que permitieran manejar la complejidad que se
estaba precisando, pero los requerimientos crecieron en complejidad. Es obvio que una crisis as no va a
finalizar nunca (aunque personalmente creo que se debera hacer todo lo posible por acotarla ).
Desde el punto de vista de la complejidad y del riesgo de error, podemos hablar de categoras de
aplicaciones. En primer lugar, estaran los programas aficionados, que son los que se hacen por individuos
dentro de grupos que comparten intereses comunes, como los estudiantes: entre stos, el costo del error es bajo
y el desarrollo es econmico. Luego vendran los programas renovables o de consumo, como los tpicos
productos de procesamiento de textos y planillas de clculo, de un diseo con poca interaccin con el usuario
real, muy econmicos por su gran difusin, y de escasos riesgos en caso de errores. Le siguen los llamados
sistemas esenciales, que seran aquellos que con sus fallas pueden poner en peligro el funcionamiento de una
empresa. Y finalmente, los sistemas vitales, de los que depende la vida de seres humanos o los que pueden
poner en peligro la vida humana. Evidentemente, cuanto mayor es el riesgo de error, mayor es la complejidad,
pues las salvaguardias para evitar errores introducen gran cantidad de cdigo y relaciones entre mdulos.
El aumento de la complejidad no es un asunto trivial. Si pensamos, haciendo una grosera
simplificacin, que cada nueva funcionalidad agregada a un producto de software agrega un nuevo mdulo que
implemente esa funcionalidad, podemos medir el aumento de complejidad asimilndolo al aumento del nmero
de mdulos. Pero, como sabemos, la cantidad de interacciones posibles entre mdulos crece mucho ms que
linealmente con stos, y es del orden del cuadrado del nmero de mdulos 10 .
Encima, lo usual es enfrentar la complejidad, no intentando escribir la menor cantidad de cdigo
posible, sino que se suele traducir en un aumento, a veces innecesario, del tamao del cdigo.
Otras razones para el uso de nuevas tcnicas de desarrollo tienen que ver con nuevos usos de la
9
10
- 13 -
CARLOS FONTELA
informtica o nuevas formas de uso. Por ejemplo, el auge de las interfaces grficas de usuario, sobre todo luego
de 1995, ha impulsado nuevas formas de desarrollo, entre ellas la programacin visual y la programacin por
eventos. La World Wide Web tambin ha impulsado la aparicin de aplicaciones especiales para ese medio.
Estas tcnicas, a su vez, precisan de nuevas metodologas de desarrollo y programacin.
Calidad del software
El objetivo primordial de la orientacin a objetos es la calidad. Todo lo dems le est subordinado.
Para entender la importancia de la calidad, mencionemos el problema del ao 2000. Debido a este
grueso error de diseo, que muchos informticos tratan intilmente de justificar, se gastaron miles de millones
de dlares en todo el mundo en mantenimiento correctivo. Hemos pensado en los usos alternativos que pudo
haber tenido esa cantidad de dinero?
La calidad del software est influenciada tanto por factores internos, que son aqullos que slo
afectan a los informticos, y externos, que perciben los usuarios finales. Estos ltimos son los nicos realmente
importantes, y los primeros deben ser condicin para que se den estos.
A continuacin presento una lista de factores, de ms a menos importantes:
?? Confiabilidad. ste es el factor de calidad por excelencia. Se basa a su vez en dos necesidades:
correccin (que el sistema cumpla con las especificaciones) y robustez (capacidad de reaccionar
bien ante situaciones excepcionales).
?? Extensibilidad: es la facilidad de adaptarse a cambios de especificacin.
?? Reutilizacin: es la capacidad de las partes de un sistema para servir en la construccin de
aplicaciones distintas.
?? Compatibilidad: se refiere a la interoperabilidad con otros productos de software, y se logra a
travs del uso de estndares, como las normas de comunicacin, formatos de archivos, etc..
?? Facilidad de uso o usabilidad: significa que el sistema pueda ser utilizado por personas de
diferentes formaciones y capacidades.
?? Portabilidad: es la facilidad de correr el sistema en otras plataformas distintas de aquellas para las
cuales fue diseado.
?? Eficiencia. Consiste en usar la menor cantidad posible de recursos de hardware, tiempo de
procesador y ancho de banda. Este requisito de calidad no se debe exagerar, ya que, como dice
un refrn conocido, no importa cun rpido es hasta que no est correcto.
?? Funcionalidad. El sistema no debe pecar ni por exceso ni por defecto respecto de los
requerimientos. En general, conviene comenzar el desarrollo por los aspectos ms importantes, y
nunca sacrificar la calidad por agregar funcionalidad.
?? Oportunidad: que el sistema est en el mercado en el momento en que los usuarios lo precisan, o
apenas un poco antes.
?? Costo.
ORIENTACIN A OBJETOS
La tecnologa de orientacin a objetos es un nuevo paradigma de desarrollo de sistemas. Con esta
tecnologa se busca esa meta siempre inalcanzable de obtener sistemas econmicos de desarrollar y mantener11 .
En realidad, el paradigma de objetos no es nuevo en absoluto, aunque su auge periodstico haya
empezado hace unos 10 aos. Naci de la mano de los problemas de simulacin, y de lenguajes como Flavors y
11
Cuando digo "inalcanzable" me refiero a que cada unos 10 aos surgen nuevas ideas que mejoran las tcnicas que se estaban
utilizando y que supuestamente eran el ltimo escaln en este sentido.
- 14 -
Smalltalk, hace ya ms de 20 aos. Tampoco es cierto que sea una idea totalmente diferente de la
"programacin tradicional", al punto que quien quiera aprender a programar con objetos deba sepultar sus
conocimientos previos en programacin, como pretenden algunos autores apasionados por el paradigma.
Lo que se ha gestado en estos ltimos aos es una fiebre de consumo (periodstico por lo menos) de
todo lo que de alguna manera tenga algo que ver con los objetos. Y, paralelamente, han aparecido en el
mercado un sinnmero de ambientes de trabajo, ambientes de desarrollo e implementaciones de lenguajes que
se proclaman "orientados a objetos". Y eso es lo que la mayora ha odo: mucho ruido, personas que hablan de
algo que no entienden del todo y mucha confusin.
La metodologa de orientacin a objetos se impuls con una serie de objetivos, que analizaremos en
los tems subsiguientes:
?? Reduccin de la brecha entre el mundo de los problemas y el mundo de los modelos.
?? Aumento del nivel de complejidad de los sistemas.
?? Fomentar la reutilizacin y extensin del cdigo.
?? Uso de prototipos.
?? Programacin en ambientes de interfaz de usuario grfica.
?? Programacin por eventos.
Reduccin de la brecha entre el mundo de los problemas y el mundo de los modelos
La orientacin a objetos intenta crear una correspondencia unvoca entre elementos del espacio del
problema y objetos en el espacio de soluciones.
Desde la perspectiva de la orientacin a objetos, los objetos de un programa interactan para resolver
un problema, respondiendo a estmulos mensajes o eventos del medio externo (generalmente otros
objetos).
Para la mayora de las personas, la forma de pensar orientada a objetos es ms natural que las
tcnicas tradicionales. Al fin y al cabo, el mundo est formado por objetos. Comenzamos a aprender sobre ellos
en la infancia y descubrimos que se comportan de determinadas maneras: desde una etapa muy temprana
categorizamos los objetos y descubrimos su comportamiento. Las personas piensan de manera natural en
trminos de objetos, eventos y mecanismos de activacin. Y los programas no son ms que modelizaciones de
la realidad.
Miremos un poco alrededor de nosotros: las sillas en que estamos sentados, la mesa que tenemos
delante, el colectivo que nos transporta, el supermercado donde hemos ido a comprar comida, las cajas, el pollo
y los tomates que compramos, los cajeros... Estamos rodeados por objetos.
sta es una de las principales razones por las cuales los ambientes grficos se suelen proclamar como
orientados a objetos.
Si tuviramos que hacer un sistema que simulara las colas en las paradas de colectivos o los stocks
del supermercado, nadie dudara en utilizar POO. Es por ello que el primer lenguaje que tuvo cierta orientacin
a objetos fue Simula 67, un lenguaje orientado a simulacin, en la dcada del sesenta!
Un objeto es cualquier cosa, real o abstracta, acerca de la cual almacenamos datos y los mtodos que
controlan dichos datos. Son ejemplos de objetos: una empresa, un recibo, un plano de la Repblica Oriental del
Uruguay, una disquetera, un tranva, el recorrido de un tranva, un proceso para emitir un recibo, un icono en
una pantalla, toda la pantalla, este texto, un carcter del texto, etc.
En el ambiente de la POO se suele pensar en los objetos como entidades activas, que realizan tareas.
A los datos de la programacin tradicional se les aplican procesos, se les hace tal cosa, se les hace tal otra...
Cuando se trabaja con objetos son stos los que actan, estimulados por mensajes y respondiendo con el
comportamiento esperado. Se piensa en los objetos como actores en el escenario, a los cuales el programador
(autor) les define su comportamiento (papel) y el usuario (director) dirige su actuacin.
- 15 -
CARLOS FONTELA
Tambin se puede pensar en un objeto como un elemento a priori pasivo, que es despertado por la
llegada de un mensaje proveniente de otro objeto, y que responde activamente a este mensaje.
Una clase responde a la idea de concepto, entendido como una nocin compartida que se aplica a
determinados objetos en forma consciente. Un concepto puede no tener instancias, como el concepto de
elefante ovparo . Sin embargo, no puede haber objetos que no respondan a un concepto. As surge la nocin de
clase.
Desde esta perspectiva, los objetos de un programa interactan para resolver un problema,
respondiendo a estmulos mensajes o eventos del medio externo (generalmente otros objetos).
Aumento del nivel de complejidad de los sistemas
Como dijimos ms arriba, sta es una de las principales causas que provocan la continuidad de la
crisis del software. Desde el principio de esta crisis se intent atacar el problema de la complejidad, en un
principio mediante la descomposicin descendente (top-down) de un sistema, y luego incorporando la
construccin ascendente (bottom-up) de mquinas virtuales cuyas abstracciones fueran acercando el modelo al
problema en forma iterativa.
La orientacin a objetos, al reducir la brecha entre los modelos y la realidad, ya colabora en este
aspecto. Adicionalmente, un mejor manejo de los grandes bloques de cdigo, la agrupacin de TDA,
bibliotecas y dems, facilita la administracin de proyectos grandes. Tampoco es despreciable la facilidad para
el trabajo de grupos grandes de desarrolladores.
Pero lo que cambia radicalmente la tecnologa de orientacin a objetos es nuestra forma de pensar
sobre los sistemas. La complejidad de los objetos que podamos utilizar seguir en aumento, ya que nuevos
objetos se pueden construir a partir de otros. stos a su vez estn constituidos por otros objetos, y as
sucesivamente. Podemos tener una biblioteca como un depsito de clases, en parte comprada y en parte
construida en casa. Es muy probable que estas clases sean ms poderosas a medida que crece su complejidad.
De esta manera, se puede llegar a un sistema de un milln de lneas de cdigo adquiriendo novecientas mil en
el mercado y desarrollando las cien mil ms especficas de la aplicacin en cuestin.
Por todo esto hay quienes dicen que la orientacin a objetos permite ocultar la complejidad bajo la
simplicidad de los objetos.
Reutilizacin y extensin del cdigo
Llamamos reutilizacin y extensin del cdigo a la facilidad para emplear, en el desarrollo de una
aplicacin, cdigo desarrollado y probado en otros contextos. Volveremos sobre esto en captulos posteriores.
La orientacin a objetos viene incluye dos capacidades, denominadas herencia y polimorfismo, que
facilitan enormemente el uso de cdigo escrito, ya sea por el propio equipo o por terceros, sin necesidad de
copiarlo fsicamente y sin violar el principio de ocultamiento de implementacin.
A su vez, este cdigo puede ser utilizado tal como est (reutilizacin) o se le pueden incorporar
nuevas funcionalidades sin alterarlo (extensin), gracias de nuevo a la herencia y el polimorfismo.
Uso de prototipos
Como veremos ms adelante, las nuevas metodologas de desarrollo de software estn virando hacia
un desarrollo iterativo e incremental con amplia participacin de los usuarios. En este contexto, el concepto de
prototipo, como versin intencionalmente incompleta de un producto, surge con mucho nfasis.
Las tcnicas de orientacin a objetos facilitan el uso de prototipos debido a la facilidad de
implementar un sistema en forma absolutamente modular, e incluso dejar detalles de implementacin para ms
adelante. Es la caracterstica denominada encapsulamiento la que, al preocuparse ms bien de qu hace el
sistema ms que de cmo lo hace, permite dejar de lado detalles finos y concentrarse en las lneas generales del
sistema. La herencia y el polimorfismo son importantes si se posee una jerarqua de clases rica, ya que
favorecen el desarrollo veloz de clases similares a las ya existentes, trabajando siempre sobre comportamiento
- 16 -
probado.
Programacin en ambientes de interfaz de usuario grfica
Las interfaces de usuario grficas no son nuevas. Xerox lanz el primer ambiente de este tipo con la
Xerox Star en 1981. Su sucesor ms famoso fue el sistema operativo de la Macintosh de Apple, en 1984. Poco
despus surgira una interfaz de usuario que corra sobre DOS para PC, llamada GEM (1985), muy parecida al
producto de Apple , y a ella le siguieron el ambiente Windows de Microsoft (nacido en 1985 pero usado en
forma ms masiva a partir de 1990), que tambin corra sobre DOS, y el Warp del sistema operativo OS/2 de
IBM, por nombrar slo a los ms conocidos. Sin embargo, la aparicin del robusto Windows NT en 1993, la
conversin del simple ambiente Windows en sistema operativo en 1995 y, finalmente, la proliferacin de
interfaces grficas para sistemas Unix, han dado por tierra con las interfaces de lnea de comandos que parecan
inmortales hace apenas 10 aos.
Estas interfaces de usuario se conocen como WIMP (windows, icons, mouse pointer) o GUI
(graphical user interface). Y la interfaz web es un caso especial de las mismas.
Uno de los objetivos de la orientacin a objetos es facilitar el desarrollo de software para estas
plataformas GUI y web, ya que en este tipo de ambientes se manipulan ms bien objetos y no tanto procesos.
Programacin por eventos
Pero las caractersticas de las interfaces de usuario grficas no se reducen al manejo de ventanas,
iconos y el mouse, ni tampoco a su condicin grfica.
En realidad, desde el punto de vista de la programacin, su mayor novedad es la forma en que el
usuario interacta con la aplicacin, que modifica totalmente la forma tradicional secuencial en que la
computadora ejecutaba una serie de tareas a pedido del usuario y cada tanto le peda un dato para continuar. En
estos nuevos enfoques, basados en eventos, el usuario tiene toda una gama de acciones que puede realizar y la
aplicacin debe estar preparada para responder con acciones previstas pero no programadas.
Aqu la programacin orientada a objetos aporta su visin no imperativa de desarrollo, que permite
no fijarse tanto en los procesos como en los mensajes enviados por el usuario y en las respuestas de la
aplicacin a esos estmulos.
- 17 -
CARLOS FONTELA
Dado que esos mtodos son, en la prctica, muy similares a los procedimientos de la programacin
imperativa tradicional y los mensajes se podran pensar como invocaciones a esos procedimientos, se podra
pensar que simplemente se ha inventado un nuevo nombre para algo que ya exista anteriormente.
Esto es cierto: las ideas de objetos provienen en gran parte de la abstraccin de datos. Sin embargo,
el paradigma de objetos introduce conceptos nuevos. Adems, es una tcnica ms estructurada que los intentos
anteriores de estructuracin. Y es ms modular y abstracta que los intentos previos de abstraccin de datos y
ocultamiento de implementacin.
La POO es un paradigma de programacin no imperativa, no procedimental.
Se basa en tcnicas previas, como la abstraccin y el ocultamiento de la implementacin, e incorpora
la herencia, el polimorfismo y otras de menor entidad.
No entraremos en mayor detalle porque dedicaremos 4 captulos a este tema.
S digamos que la POO no se puede desligar de todo el paradigma de orientacin a objetos. En
efecto, no hay que confundir POO con el uso de un lenguaje que soporte POO. Para hacer buena POO hay que
desarrollar todo el sistema utilizando el paradigma, empezando por un anlisis y un diseo orientados a objetos.
Lenguajes de programacin orientada a objetos
Ms adelante haremos un examen ms detenido de los principales lenguajes de POO, una vez que
hayamos estudiado los conceptos fundamentales. Aqu solamente presentamos un esquema clasificatorio de los
mismos.
Los lenguajes que se usan para la POO se pueden clasificar de muchas maneras. Nosotros hemos
elegido una clasificacin segn su origen y afinidad con el paradigma.
En primer lugar, tenemos los denominados lenguajes puros de orientacin a objetos. Todos ellos
provienen del antiguo Simula 67 y tienen como caracterstica permitir slo objetos como tipos de datos. Entre
ellos estn:
?? Simula
?? Smalltalk
?? Eiffel
Luego existen lenguajes que permiten tanto la POO como algn tipo de programacin procedimental,
o bien que permiten algunos datos que no son objetos. Dentro de este grupo hay algunos que deriva n de los
lenguajes procedimentales estructurados Pascal, C y Ada 86 (que a su vez deriva de Pascal):
?? Objective C
?? C with classes
?? C++
?? C#
?? Clascal
?? Object Pascal
?? Ada 95
?? Modula-3
?? Java
Otros de este grupo derivan del lenguaje funcional y de listas Lisp, de gran popularidad en algunos
ambientes en la dcada de 1980 y considerablemente ms cerca de la POO que los lenguajes estructurados:
?? Loops
?? Flavors
?? Common Loops
?? CLOS
- 18 -
?? Ceyx
Finalmente, hay algunos lenguajes que permiten utilizar objetos, pero no soportan todo el paradigma
de orientacin a objetos. Algunos de ellos no permiten definir clases, otros no soportan herencia o
polimorfismo. He aqu algunos ejemplos:
?? Simula 67
?? Clu
?? Ada 86 y anteriores
?? Visual Basic (hasta versin 6)
?? Javascript
Por supuesto, hay otras maneras de clasificar a los lenguajes de POO. Una de ellas es el manejo de
tipos. En ese sentido, Ada y Eiffel son de tipeo fuerte, pues slo permiten combinar expresiones del mismo
tipo. En el mismo camino, aunque siendo menos estrictos, estn Object Pascal, Modula-3 y Java. C++, en
cambio, maneja un tipeo dbil, un poco por compatibilidad con su predecesor C. En estos lenguajes se permiten
combinar expresiones de tipos distintos y la transformacin de tipos se puede hacer casi sin restriccin. Hay
tambin lenguajes sin tipos, como Smalltalk y Python.
La discusin sobre el grado de tipeo es tan larga como la historia de la programacin. La gran ventaja
del tipeo fuerte es que, al no permitir mezclar conceptos diferentes 12 hace ms fcil la depuracin y el
mantenimiento. Sin embargo, los lenguajes de tipos dbiles o sin tipos permiten generar aplicaciones pequeas
muy rpidamente, y por lo tanto son candidatos ideales para construir prototipos.
En el curso, como ya he dicho en el prlogo, utilizamos los lenguajes Object Pascal, C++ y Java.
Sabemos que son lenguajes que tienen sus races en otros paradigmas, pero precisamente es el paradigma que
presumimos que manejan nuestros lectores. Pensamos que ensear Simula, Smalltalk, Eiffel o cualquier otro
lenguaje hara ms difcil el seguimiento por parte de ellos. Si este fuera un primer curso de programacin
deberamos quedarnos con un lenguaje de POO puro, que permita pasar de los conceptos al cdigo de manera
natural, como ocurra con Pascal en la programacin estructurada, y dejar para un segundo curso el estudio de
lenguajes hbridos de uso comercial.
A menudo he mostrado la evolucin de una idea a partir de Pascal o Ada. Pascal, por ser un lenguaje
archiconocido en el mbito de la programacin estructurada. Ada, porque es fuente de muchos avances que
luego llegaron con los objetos, al punto que el fracaso de Ada tuvo que ver en gran parte con que cuando el
mercado estaba maduro para empezar a utilizarlo, empez la revolucin de los objetos; y porque Ada se parece
bastante a Pascal.
Muchos lectores se preguntarn por qu no se ha analizado el lenguaje Ada 95, proviniendo como
proviene de uno de los ms avanzados lenguajes estructurados. La verdad es que a los que agregaron la
orientacin a objetos a un lenguaje tan complejo como Ada 86 no se les ocurri mejor idea que hacerlo mucho
ms complicado an. Los propios creadores de la primera versin de Ada se vieron defraudados con esta nueva
versin. Encima, ni siquiera soporta todos los aspectos del paradigma.
Y el resultado comercial fue todo lo decepcionante que poda esperarse. Ada 95 no ha pasado de ser
un objeto de anlisis acadmicos. As como Ada 83 se adelant mucho a lo que la industria esperaba de un
lenguaje, y luego fracas parcialmente por su carcter revoluciona rio, Ada 95 fracasa por no poder romper con
sus antecedentes procedimentales y por convertir conceptos simples de la orientacin a objetos en
implementaciones tremendamente complicadas.
Herramientas visuales y de desarrollo rpido de aplicaciones
Al hablar de POO no podemos dejar de lado la programacin visual, tambin conocida como
12
Por ejemplo, no debera permitirse sumar una velocidad a un monto en pesos, aunque ambos sean nmeros reales.
- 19 -
CARLOS FONTELA
desarrollo rpido de aplicaciones (RAD es la sigla en ingls). Se entiende por tal a herramientas que permiten
el desarrollo de sistemas sin escribir acciones en un lenguaje de programacin, sino construyendo prototipos de
pantallas y eventos, y utilizando facilidades para acceder a bases de datos y componentes. Esto no solamente
facilita el desarrollo sino que permite una fcil experimentacin con las interfaces de usuario posibles de un
sistema.
A menudo se considera que la aparicin de estos ambientes de programacin (no sera del todo
correcto denominarlos lenguajes), que acompaan el auge de las interfaces de usuario grficas, significan la
partida de defuncin de los lenguajes de programacin.
Sin embargo, pronosticar la desaparicin de los lenguajes de programacin nos parece apresurado.
No se debe dejar de lado el hecho de que todos estos ambientes cuentan con un lenguaje de fondo, el que se
utiliza para desarrollar mdulos que realizan las acciones no visuales (u ocultas) que todo sistema hace.
Es cierto que, con productos RAD, muchas aplicaciones se pueden desarrollar en tiempos muy
cortos, pero se trata de aplicaciones de mucha interfaz de usuario , como pasa en la Web, que tambin tiene sus
herramientas de desarrollo rpido.
Lo que s queda claro es que cada vez se van a utilizar ms estos ambientes que dejarn a cargo del
programador, casi exclusivamente, los mdulos que realizan tareas ocultas. Probablemente, el desarrollo de
interfaces de usuario mediante cdigo escrito a mano cada vez represente una peor inversin del tiempo de
programador.
Esto enfatiza an ms nuestra preocupacin por la programacin modular, especialmente ascendente
y con objetos 13. Al fin y al cabo, los sistemas medianos realizan un gran nmero de tareas que poco tienen que
ver con la interfaz de usuario o los accesos a datos. En estos sistemas, tal vez tenga que haber programadores,
que desarrollen los aspectos ms avanzados de la aplicacin, y lo que se denominan usuarios del ambiente de
desarrollo14 , que se encarguen de la preparacin de interfaces de usuario y accesos a datos. Estos usuarios
podrn tener una formacin ms cercana al desarrollo de interfaces de usuario que al desarrollo de sistemas.
Uno de los problemas con las herramientas RAD es que se busca simplicidad a costa de falta de
originalidad, lo cual puede ser bueno, al reducir el costo de aprendizaje de un nuevo sistema, pero es malo al
desincentivar la creacin de interfaces de usuario orientadas a cada modelo de negocio.
Pero el principal problema que tiene, y que hace que deba plantearse seriamente su solucin, es que
los sistemas desarrollados con herramientas visuales son muy difciles de mantener y de reutilizar. Esta
deficiencia se debe sobre todo a la poca claridad del cdigo. Hay algunas excepciones, no obstante, entre las
cuales destaca la biblioteca Swing para desarrollar aplicaciones de interfaz de usuario grfica en Java 2.
Esta visin a corto plazo de los usuarios de herramientas RAD nos est llevando otra vez a los
lejanos aos anteriores a la primera crisis del software. Hoy por hoy, lo nico que garantiza la modificabilidad
de una aplicacin desarrollada con herramientas RAD es una muy buena documentacin.
13
14
- 20 -
Estas definiciones no son estrictas. Hay quienes consideran como estado slo a lo que puede cambiar en el tiempo.
Aunque un objeto puede tener varios, como ocurra con las variables que tenan sinnimos.
- 21 -
CARLOS FONTELA
ENCAPSULAMIENTO
La abstraccin en programacin orientada a objetos
Todos los lenguajes que soportan POO permiten establecer la abstraccin a nivel de clases,
declarando en su interior tanto la estructura como el comportamiento. Por ejemplo, en Java se definira una
clase para implementar un perodo de tiempo como sigue:
public class cPeriodo {
// atributos o estructura
TFecha desde, hasta;
// mtodos o comportamiento
int cantidadDias () {
// cdigo de la funcin
}
...
}
- 22 -
De esta manera, es fcil crear un TDA, definiendo todo en un mismo bloque de cdigo 17.
El ocultamiento en programacin orientada a objetos
Todos los lenguajes que soportan POO tienen, por lo menos, dos categoras de acceso de atributos y
mtodos, denominadas pblico y privado. Como vimos, lo conveniente es que los mtodos sean de acceso
pblico y los atributos privados.
Por ejemplo, en Java se declarara:
class cPeriodo {
// atributos inaccesibles desde fuera:
private TFecha desde, hasta;
// mtodo visible desde otras clases:
public int cantidadDias () {
// cdigo de la funcin
}
...
}
Como dijimos antes, no podemos pretender hacer un genuino ocultamiento si no proveemos al cliente
de toda la funcionalidad que puede llegar a necesitar.
Esto est relacionado con la nocin de atributo conceptual, que es todo aquello que un usuario
presuma que debe existir como atributo, aun cuando no exista explcitamente. Por ejemplo, un nmero
complejo se puede implementar con dos atributos que representen el mdulo y el argumento, pero las partes
real e imaginaria, aunque no sean atributos fsicos, son atributos conceptuales, pues todo cliente presumir que
existen. A menudo estos atributos son llamados propiedades.
En consecuencia, para garantizar el ocultamiento de la informacin, todo atributo conceptual debe
tener un mtodo selector para obtener su valor y un mtodo modificador para alterarlo.
Encapsulamiento
En POO, a la conjuncin de abstraccin y ocultamiento de implementacin se la llama
encapsulamiento .
Segn Booch [6], encapsulamiento es el proceso de almacenar en un mismo compartimiento los
elementos de una abstraccin que constituyen su estructura y su comportamiento; sirve para separar la interfaz
contractual de una abstraccin y su implantacin.
Esto es ms cierto en Java que en Object Pascal, donde slo se declaran los encabezados de los mtodos. En C++ se puede hacer de
las dos maneras.
- 23 -
CARLOS FONTELA
Por ejemplo, en una aplicacin con ventanas, si las modificaciones de color deben afectar
simultneamente a todas las ventanas, el atributo Color sera un atributo de clase. En este caso se tratara de un
atributo de clase cuyo valor puede variar, pero cuando vara lo hace para todas las instancias de la clase a la
vez.
Otro ejemplo podra ser declarar la clase de los nmeros reales, como derivada de los complejos,
pero con parte imaginaria igual a cero. En este caso se tratara de un atributo de clase constante.
A veces se los llama atributos estticos.
Atributos con valor constante
A veces ocurre que en una clase un determinado atributo debe tener siempre el mismo valor.
Por ejemplo, en una aplicacin comercial, un identificador de cliente, que no se modifica mientras
exista el cliente. En este caso se trata de un atributo constante que es distinto para cada instancia.
Otro ejemplo podra ser el que definimos anteriormente de los reales y los complejos. En este caso
sera adems un atributo de clase.
Atributos con restricciones
A veces algunos atributos tienen que cumplir una restriccin en su valor. La restriccin puede ser
absoluta o estar vinculada a otro atributo. Estas condiciones que deben cumplir los atributos suelen llamarse
invariantes.
Por ejemplo, una restriccin absoluta sera imponer que un atributo que contiene un monto sea
siempre positivo.
Un ejemplo de restriccin vinculada a otro atributo se podra dar si, por razones especiales, se
decidiera implementar una clase que almacene un perodo, guardando simultneamente la fecha de inicio, la de
finalizacin y la duracin.
Siempre que haya restricciones sobre atributos, hay que verificar el cumplimiento de la restriccin en
todas las operaciones de modificacin, incluyendo el constructor. Una posibilidad interesante es encapsular los
atributos que tienen restricciones interrelacionadas en una clase aparte.
Mtodos de clase
Un concepto un poco menos til que el del atributo de clase es el de mtodo de clase. Se define as
al mtodo que no puede acceder a atributos de instancia y del cual existe una sola versin posible para toda la
clase.
Tambin se utilizan mtodos de clase cuando se pretende usar POO estricta (sin variables que no
sean objetos ni subprogramas que no sean mtodos), pero a la vez se desea llamar un mtodo (subprograma) sin
usar un objeto.
En realidad, con los mtodos de clase se estn utilizando subprogramas en forma global. Por eso, si
vemos muchos mtodos de clase en un sistema, tal vez no estemos haciendo un autntico desarrollo orientado a
objetos.
A veces de los llama mtodos estticos.
TERMINOLOGA DE OBJETOS
Cada vez que surge una nueva tecnologa, son muy raros los casos en que sus primeros impulsores se
resisten a la tentacin de crear toda una gama de nuevos trminos relacionados con ella. As, de la mano de la
programacin estructurada, por ejemplo, el trmino accin reemplaz al trmino sentencia ; procedimiento
reemplaz a subrutina. La tecnologa de objetos por cierto no es una excepcin.
Por supuesto que esto tiene su razn de ser: los trminos accin y sentencia , por ejemplo, si bien
evocan conceptos parecidos, no significan lo mismo. Adems, utilizar una nueva terminologa facilita el
- 24 -
OPERACIONES
Estado observable de un objeto
Las operaciones que se aplican a un objeto o clase, se pueden clasificar en operaciones de consulta ,
que son los mtodos que no alteran el estado de un objeto, y las de modificacin , que son las que s pueden
alterar el estado de un objeto.
Se llama estado observable al resultado de la aplicacin de todas las operaciones de consulta. Puede
no coincidir con el estado real cuando algunos atributos no sean accesibles mediante operaciones de consulta o
cuando las operaciones de consulta permitan definir otros atributos conceptuales.
18
Hay bibliografa que distingue los conceptos de mtodo y operacin, dejando el primero para la implementacin de una operacin, y
define a la operacin como un servicio que se puede requerir de cualquier objeto de una clase.
- 25 -
CARLOS FONTELA
- 26 -
class cElipse {
void cElipse () {
// cuerpo de cElipse, sin parmetros
}
void cElipse (int Radio1, int Radio2) {
// cuerpo de cElipse, con 2 parmetros enteros
}
void cElipse (double Radio1, double Radio2) {
// cuerpo de cElipse, con 2 parmetros double
}
// int cElipse ()
// sera invlido porque difiere slo en el tipo devuelto
// otras definiciones de atributos y mtodos
}
En este caso hemos sobrecargado el constructor de cElipse, pero puede hacerse con cualquier mtodo
de la clase.
Pero incluso en C++ y Object Pascal est permitida la sobrecarga, y aun en subprogramas declarados
fuera de una clase. Por ejemplo, los siguientes encabezados de funciones son vlidas en Object Pascal en forma
simultnea:
function Potencia (X: Real; N: Integer) : Real; overload;
function Potencia (M: Integer; N:Integer) : Integer; overload;
function Potencia (X: Real; Y: Real) : Real; overload;
Ntese que en Object Pascal es necesario poner la palabra overload, cosa que no existe en C++ y
Java.
En C++ est permitido sobrecargar operadores, una idea tomada de Ada. Al fin y al cabo, los
operadores son funciones (en la suma de enteros, + es una funcin entera de dos parmetros enteros). Por
ejemplo, en la implementacin de complejos anterior se podra haber substituido la funcin Suma por una
sobrecarga al operador + de esta forma:
cComplejo cComplejo :: operator+ (cComplejo v)
{
cComplejo aux;
aux.re = re + v.re;
aux.im = im + v.im;
return aux;
Los operadores sobrecargados se heredan a las clases descendientes, salvo el operador de asignacin
y los de asignacin de memoria new y delete. Hay algunas propiedades interesantes, como que el operador de
desigualdad no se puede sobrecargar, sino que devuelve lo contrario del operador de igualdad.
- 27 -
CARLOS FONTELA
Se ha hablado muy mal de la sobrecarga de operadores en C++, de lo poco til que resulta y de los
problemas que acarrea. Sin embargo, bien usados pueden aclarar bastante el uso de determinadas funciones.
Incluso los operadores de asignacin, de igualdad y desigualdad y los de comparaci n se podran sobrecargar
siempre para darles el significado que deberan tener en cada clase. As hicimos nosotros en los ejercicios
resueltos en C++. Lo que ocurre es que muchos programadores han usado los operadores para cualquier cosa,
bastardeando el sentido original de los mismos. Por eso, cuando se trabaja con sobrecarga de operadores, ms
an que con sobrecarga de funciones en general, debe respetarse la semntica del operador original.
En realidad, el fragmento anterior slo declara la clase, pero la implementacin debe definirse
luego 19 :
{ ... implementaciones anteriores ... }
procedure cComplejo.Resta (X,Y: cComplejo);
begin
Re := X.Re Y.Re; Im := X.Im Y.Im
end;
function cComplejo.Modulo : Real;
begin
Modulo := Sqrt(Re*Re+Im*Im)
end;
{ ... continan las implementaciones ... }
19
Si estamos trabajando en una unidad de biblioteca, la declaracin se suele poner en la parte interface y la implementacin debe
definirse en la implementation.
- 28 -
- 29 -
CARLOS FONTELA
Obviamente, esta herramienta sirve tambin para definir propiedades que no estn directamente
atadas a un atributo. O, lo que es lo mismo, podran usarse para definir atributos conceptuales. Ntese que si
implementamos la clase cComplejo con un par de valores para sus partes real e imaginaria, podramos definir
propiedades para el mdulo y el argumento, en este caso como de slo lectura:
property PModulo:Real read Modulo;
property PArgumento:Real read Argumento;
Es en este sentido que decimos que sirve para mejorar el encapsulamiento. Pero no debieran usarse
las propiedades para eludir el encapsulamiento.
Las propiedades pueden tener un valor por defecto, pero no si son de slo lectura.
De todas maneras, por ser una capacidad exclusiva de Object Pascal y alguno que otro lenguaje 20 , no
entraremos en detalle.
Objetos y clases en C++
En C++ un objeto se declara como si fuera una variable, y una clase con la palabra reservada class.
Por ejemplo, el siguiente fragmento declara z como instancia de cComplejo, a la vez que llama al
constructor cComplejo pasndole como argumentos 2 y 3:
cComplejo z (2,3);
Ntese que en la declaracin de la clase no se suelen poner las implementaciones de los mtodos, que
se definen luego (aunque nada impide implementarlos en la propia declaracin, al estilo Java), como en el
ejemplo que sigue:
// ... implementaciones anteriores ...
void cComplejo :: resta (cComplejo x, cComplejo y);
{
re = x.re - y.re; im = x.im - y.im;
}
void cComplejo :: modulo()
{
return sqrt(re*re+im*im)
// presumimos la existencia de la funcin sqrt
}
20
Como C#.
- 30 -
private:
int x;
public:
c();
21
22
En realidad, la definicin de una funcin en lnea deja en manos del compilador si la expande o no, en base, por ejemplo, a su tamao.
A veces se las llama funciones de ayuda.
- 31 -
CARLOS FONTELA
~c();
friend int a (void);
}
En el caso anterior, la funcin a es amiga de la clase c y puede acceder al atributo x. Sin embargo, a
no es un mtodo miembro de c, y slo se est escribiendo un prototipo de a en la declaracin de c. La funcin a
se declarar ms adelante como cualquier funcin de C o C++.
Puede parecer algo intil la posibilidad de declarar funciones amigas. Sin embargo, pueden utilizarse
cuando se desea que una funcin acceda a atributos privados de ms de una clase. En este caso se la declara
amiga de ambas y se habr solucionado el problema.
C++ permite la definicin de clases internas a otras clases. Una clase interna puede utilizar mtodos
y atributos privados de su clase externa, con la cual no guarda ninguna relacin de herencia (veremos herencia
en el prximo captulo). Esto permite vincular clases muy acopladas, de modo de facilitar el desarrollo y
mantenimiento de las mismas. Esta propiedad no la utilizaremos en este curso.
Un atributo de clase (o esttico) se indica en C++ anteponindole la palabra static en su declaracin.
Tambin pueden declararse del mismo modo mtodos de clase (o esttico).
Un atributo con valor constante se indica ponindole la palabra const en su declaracin, a
continuacin del nombre.
Los atributos con restricciones no estn implementados en C++, por lo que debe hacerlo el
programador, asegurando que tanto el constructor como todos los mtodos modificadores no puedan alterar la
restriccin.
A continuacin se muestran una declaracin de un mtodo y un atributo de clase, un atributo con
valor constante y un atributo con valor por defecto:
class cVentanaEspecial
{
// atributo de clase
En C++ hay tambin formas de especificar objetos constantes cuyo estado no puede variar y
mtodos que no pueden modificar el estado de un objeto, siempre usando la palabra const.
Objetos y clases en Java
En Java un objeto se declara como si fuera una variable, y una clase con la palabra reservada class .
Por ejemplo, el siguiente fragmento declara z como instancia de cComplejo, a la vez que llama al
constructor cComplejo pasndole como argumentos 2 y 3:
cComplejo z = new cComplejo(2,3);
- 32 -
}
public void resta (cComplejo x, cComplejo y) {
re = x.re y.re;
im = x.im y.im;
}
public double modulo() {
return Math.sqrt(re*re+im*im)
}
// ... ms operaciones ...
} ;
Ntese que en la definicin de la clase se ponen tambin las implementaciones de los mtodos. Dicho
de otro modo, se hacen en el mismo lugar la declaracin y la definicin de los mtodos de la clase.
El cliente de la implementacin de cComplejo escribir acciones como:
z.sumar(v,w); { z, v y w deben ser instancias de cComplejo }
x = z.modulo();
El concepto de paquete se estudiar ms adelante. Podemos adelantarnos diciendo que es una forma de agrupar una biblioteca de
clases en Java. El concepto de clase descendiente se ve en el captulo prximo.
24
Si la clase se llama cComplejo, el archivo ser cComplejo.java, respetando maysculas y minsculas.
- 33 -
CARLOS FONTELA
?? No hay destructores.
Java permite la definicin de clases internas a otras clases, con diversos grados de visibilidad. Incluso
se pueden definir clases locales a un mtodo y clases annimas. Una clase interna puede incluso utilizar
mtodos y atributos privados de su clase externa, con la cual no guarda ninguna relacin de herencia (veremos
herencia en el prximo captulo). Esto permite vincular clases muy acopladas, de modo de facilitar el desarrollo
y mantenimiento de las mismas. Esta propiedad no la utilizaremos en este curso.
Un atributo de clase (o esttico) se indica en Java anteponindole la palabra static en su declaracin.
Tambin pueden declararse del mismo modo mtodos de clase (o esttico).
Un atributo con valor constante se define anteponindole la palabra final en su declaracin, pero slo
si no es un objeto. Estas constantes se pueden inicializar con valores obtenidos en tiempo de ejecucin. En caso
de ser un objeto, lo que se creara sera una referencia constante, por lo que no nos sirve, y ser el programador
quien deber implementar el atributo constante en el constructor y asegurarse de que ningn otro mtodo lo
modifique. Combiando las directivas static y final declaramos constantes globales.
Los atributos con restricciones no estn implementados en Java, por lo que debe hacerlo el
programador, asegurando que tanto el constructor como todos los mtodos modificadores no puedan alterar la
restriccin.
A continuacin se muestran una declaracin de un mtodo y un atributo de clase, un atributo con
valor constante y un atributo con valor por defecto:
class cVentanaEspecial
{
- 34 -
- 35 -
CARLOS FONTELA
Ntese que estamos declarando un arreglo compuesto por figuras, de modo tal que si eliminamos el
arreglo eliminamos las figuras. Decimos que las figuras son los objetos (o variables) contenidos y el arreglo el
objeto (o variable) contenedor. Bsicamente, entre contenedor y contenido hay una relacin del tipo
contiene: un vector de figuras contie ne figuras.
La ventaja obvia de usar composicin con POO es el encapsulamiento, que nos permite no slo
declarar la estructura de los datos sino tambin las operaciones sobre los mismos. Eso se muestra en el ejemplo
siguiente:
type cFigura = class
private
{ aqu van los atributos de una figura }
public
{ aqu van constructor y destructor }
procedure Mover (X,Y:Word);
procedure Dibujar;
{ siguen ms mtodos de cFigura }
end;
type cVectorFiguras = class
private
V : array of cFigura;
- 36 -
CantFiguras : Word;
public
{ aqu van constructor y destructor }
procedure DibujarTodas;
{ dibuja todas las figuras del vector }
{ siguen ms mtodos de cVectorFiguras }
end;
Agregacin
Hay agregacin cuando una clase cumple un factor preponderante sobre otra clase. Por ejemplo, la
clase "empresa" sobre la clase "persona" (ntese que la eliminacin de la empresa no implica la eliminacin de
las personas). Es un concepto poco til, pues es una simple asociacin.
Sin embargo, muchas veces esta asociacin se representa guardando un puntero en la clase
preponderante, lo cual implica una composicin a nivel del puntero. Esto es todava ms visible en los
lenguajes en los que se representan los objetos como referencias, como Java.
El ejemplo del arreglo de figuras tambin podra haberse implementado como una simple agregacin,
si permitimos que las figuras individuales sobrevivan a la muerte del arreglo que las agrupa.
La composicin es, en realidad, un caso particular de agregacin.
- 37 -
CARLOS FONTELA
Taxonoma de animales
cAnimal
cVertebrado
cMamifero
cElefante
cInvertebrado
cAve
cPez
cMurcielago
cMolusco
cCaracol
cCrustaceo
cCalamar
Taxonoma de idiomas
cIdioma
cMonosilabico
cAglutinante
cLatino
cIndoeuropeo
cGermanico
cSemitico
cEslavo
cIndoiranio
Taxonoma de nmeros
cComplejo
cReal
cRacional
cEntero
cEnteroNegativo
cImaginario
cIrracional
cFraccionario
cNatural
- 38 -
Taxonoma de figuras
cFigura
cElipse
cCuadrilatero
cCircunferencia
cParalelogramo
cRectangulo
cTrapecio
cRectangulo
cTriangulo
cOtras
cAcutangulo
cObtusangulo
cRombo
cCuadrado
- 39 -
CARLOS FONTELA
un cuadriltero.
Pero si esta biblioteca est desarrollada utilizando objetos, lo nico que se debe hacer es extenderla
con las clases necesarias, teniendo que desarrollar muy poco cdigo. Como se ve, trabajar con POO puede
proporcionarnos un considerable ahorro de tiempo y dinero.
Para que todo esto sea estrictamente cierto, se debe construir el rbol de clases de una forma estricta,
cuidando de colocar cada clase donde deba ir. Una ayuda en este sentido es utilizar la recomendacin de que la
relacin subclase - superclase sea del tipo "es un".
Cmo hace un arquelogo que encuentra un nuevo tipo de escritura desconocido, en unas tablillas
de barro cocido, y debe determinar a qu pueblo pertenece y de qu familia es? Precisamente, debe tratar de
agregarla en el rbol de idiomas actual. Determinar si la lengua es de origen indoeuropeo, semtico u otra. Una
vez que determina que es de la familia indoeuropea tal vez deba determinar si es cltica, germnica, eslava,
escandinava, irania, ndica, mediterrnea antigua, caucsica, etc. En fin: lo mismo debemos hacer nosotros al
construir nuestro rbol de clases25 .
Herencia mltiple
Supongamos que en la jerarqua de figuras geomtricas discriminramos tipos de tringulos, y
declaramos tres clases derivadas: rectngulos, acutngulos y obtusngulos.
Sin embargo, otro programador podra necesitar una jerarqua diferente, como dividir a los tringulos
en equilteros, issceles y escalenos.
Si superponemos ambas jerarquas, llegaramos a:
CTriangulo
cTRectangulo
cAcutangulo
cObtusangulo
cIsosceles
cEquilatero
cEscaleno
cRectanguloIsosceles
El problema conceptual aqu es que, considerando ambas visiones, las clasificaciones dejaron de ser
excluyentes. Como consecuencia, si deseamos crear una clase de los tringulos que son a la vez rectngulos e
issceles, deberamos declararla con dos ancestros. Este concepto se denomina herencia mltiple .
La herencia mltiple es una herramienta poderosa de diseo y algunos lenguajes, como C++ y
Smalltalk, la soportan, mientras otros, como Java y Object Pascal, no.
Sin embargo, es una caracterstica que debe usarse con cuidado, sobre todo por las ambigedades que
se generan si una clase tiene dos padres cuyas estructuras o comportamientos se solapan, lo que ocurre ms a
menudo de lo que se cree.
Por estos inconvenientes, y por no estar disponible en todos los lenguajes orientados a objetos, no
estudiaremos la herencia mltiple.
Cuando necesitamos heredar, por un lado estructura y comportamiento, y por otro ciertos mtodos, se
puede mantener la herencia para el primer caso y hacer uso de interfaces para el segundo. Java y Object Pascal
25
- 40 -
- 41 -
CARLOS FONTELA
destructor cClase.Destroy;
begin
{ ... }
inherited Destroy
end;
En Java y en C++, en cambio, estas reglas se cumplen en forma automtica, por lo que el segmento
de cdigo no debe incluir la invocacin al constructor del ancestro, salvo cuando hay ms de un constructor en
la clase ancestro o ste tiene parmetros. Recordemos que en Java no hay destructores, pero s en C++, y los
destructores de las clases ancestros son llamados con las mismas reglas que hemos recomendado26 .
En cuanto a la composicin, conviene aprovechar el constructor de la clase contenedora para llamar
al constructor de la clase contenida. Ntese que en ningn lenguaje se garantiza que los constructores de los
atributos que son objetos contenidos sean invocados correctamente, por lo que esto queda a cargo del
programador.
Atributos y mtodos protegidos
Adems de los calificadores ya vistos, public y private, que se aplican a los atributos y mtodos para
especificar su grado de visibilidad, existe el calificador protected en los tres lenguajes en los que estamos
trabajando.
El significado de protected en C++ y Object Pascal es que la visibilidad de estos atributos y mtodos
va a ser privada, pero con el agregado de que van a ser visibles para las clases descendientes. En Java implica
que la visibilidad ser la de un atributo o mtodo de acceso friendly adems de ser visible para las clases
descendientes.
Generalizacin, especializacin e instanciacin
A estos tres conceptos los queremos distinguir claramente.
En primer lugar, la generalizaci n es la operacin a hacer para obtener el ancestro de una clase.
La especializacin, en cambio, es la operacin a hacer para obtener las subclases de una clase ms
general. Es la inversa de la anterior.
La instanciacin es la operacin a hacer para obtener un objeto de una clase. No tiene relacin
alguna con las anteriores, pues no es una relacin entre clases, sino entre una clase y un objeto o instancia. Se
obtiene un objeto individual, aunque tambin debe cumplir la relacin es un, como en la herencia .
INTERFACES
Concepto de interfaz
Las interfaces, que en algunos lenguajes se llaman protocolos, son un interesante mecanismo para
declarar algunas operaciones que deben implementar ciertas clases.
Supongamos, por ejemplo, que queremos declarar una operacin Guardar, que permita a un objeto
guardarse a s mismo en un archivo, y que pueda servir para muchas clases, aunque tenga distintas
implementaciones en cada una.
Una primera solucin sera poner el mtodo en la clase base de la familia en la cual quiero
26
- 42 -
implementar la operacin. Pero puede ocurrir que esa operacin no deba ser implementada en todas las clases
de la familia, y s se necesite implementarla en clases de otras familias. Es decir, puede haber clases cuyos
objetos sean guardables que pertenezcan a distintas familias, a la vez que no todas las clases de una familia
puedan ser guardables. En estos casos, no nos sirve el uso de herencia.
Otra posibilidad sera la herencia mltiple, en los lenguajes que la soportan. De esta manera,
podramos hacer que la clase en cuestin tuviera un ancestro que representa su familia y otro que contiene los
mtodos a implementar. Pero ocurre que estaramos violando la condicin de que la herencia exprese siempre
relaciones del tipo es un.
Las interfaces proponen una solucin ms elegante y consistente.
Una interfaz es una coleccin de operaciones que especifican un servicio. Sirve para encapsular un
conjunto de mtodos, sin asignar esta funcionalidad a ningn objeto en particular ni expresar nada respecto del
cdigo que las va a implementar. Dicho de otra manera, una interfaz describe cmo se va a utilizar un
determinado mtodo, pero no restringe qu objetos o clases lo van a poder utilizar ni cmo.
Una interfaz especifica un contrato, que permite que cambien cliente o proveedor en la medida que
cada uno cumpla lo especificado en el contrato.
Su declaracin tiene una estructura parecida a la de una clase, pero sus mtodos no se implementan,
sino que deben ser redefinidos en las clases que implementen la interfaz. Los mtodos de una interfaz deberan
ser pblicos para permitir la redefinicin 27 .
En algunos lenguajes, las interfaces pueden tener atributos, pero stos sern atributos de clase y con
valor constante.
Decimos que una clase implementa una interfaz, cuando la clase se compromete a implementar todos
los mtodos de la interfaz.
Como una clase puede implementar varias interfaces a la vez, podra haber conflictos de nombres
que se resolvern con las reglas de resolucin de mbito de cada lenguaje. No obstante, es recomendable no
repetir nombres de mtodos en distintas interfaces que sirvan a una misma familia de clases.
Las interfaces pueden tener interfaces descendientes, y una clase puede implementar interfaces a la
vez que hereda de otra clase.
Ntese que, debido a que las clases que implementan una determinada interfaz no tienen por qu ser
descendientes de un ancestro comn, esto permite algo parecido a la herencia mltiple, ya que por la herencia
se heredan los mtodos de su clase ancestro, mientras que mediante interfaces se heredan mtodos que no
provienen de una jerarqua. De todas maneras, recordemos que los mtodos provenientes de una interfaz no
tienen implementacin y deben implementarse en las clases que la utilicen.
Las interfaces estn presentes slo en algunos lenguajes de POO, como Java y Object Pascal. En
otros, como C++, deben simularse con herencia mltiple.
Por otro lado, el OMG (Object Management Group) recomienda la implementacin de interfaces
para garantizar la interoperabilidad de objetos distribuidos. Por ello es que la tecnologa CORBA hace un uso
intenso de interfaces.
Un ejemplo de interfaces: la clase adaptadora
A menudo es necesario tratar con el mismo nombre a mtodos de clases diferentes, que incluso
pueden tener encabezados diferentes.
Supongamos, por ejemplo, que tenemos una clase cLibro, provista por un servicio remoto
denominado LibreriaVirtual, que devuelve el precio de un libro llamando al mtodo precio. Simultneamente,
el servicio remoto LaGranTienda ha implementado una clase cArticulo, que incluye libros, y que tiene un
27
- 43 -
CARLOS FONTELA
mtodo esLibro, que indica si es o no un libro, y otro precioArticulo , que devuelve el precio de un artculo.
Qu ocurrira si deseo acceder a precios de libros con una sintaxis uniforme, digamos mediante un mtodo
precioLibro?
Una solucin sera construir una interfaz que luego se implementara en una clase adaptadora, como
muestra este fragmento en Java:
public interface ConsultaPrecios {
public double precioLibro();
}
Ntese que, hasta el momento, no hemos dicho nada de cmo se implementa precioLibro, sino que
slo hemos definido su encabezado, y hemos declarado que toda clase que implemente la interfaz
ConsultaPrecios deber implementar precioLibro.
Entonces ahora vamos a definir las clases adaptadoras:
// clase adaptadora para el servicio LibreriaVirtual
// archivo AdaptadoraLibreriaVirtual.java
import ar.com.LibreriaVirtual.cLibro;
public class AdaptadoraLibreriaVirtual implements ConsultaPrecios {
private cLibro libro;
public AdaptadoraLibreriaVirtual(cLibro l) {
libro=l;
}
public double precioLibro() {
return libro.precio();
}
}
// clase adaptadora para el servicio LaGranTienda
// archivo AdaptadoraGranTienda.java
import cl.com.LaGranTienda.cArticulo;
public class AdaptadoraGranTienda implements ConsultaPrecios {
private cArticulo libro;
public AdaptadoraGranTienda (cArticulo a) {
libro=a;
}
public double precioLibro() {
if (libro.esLibro())
return libro.precio();
else
throw new UnsupportedOperationException();
}
}
Las clases adaptadoras son un clsico patrn de diseo, como vamos a ver al estudiar patrones de
diseo. Pueden usarse en vez de las subclases. Incluso tienen algunas ventajas, como el hecho de que en una
clase adaptadora se puede restringir la visibilidad, cosa que nunca puede hacerse en una clase descendiente. Sin
embargo, las clases descendientes tienen ya prevista la transformacin de tipos automtica hacia arriba en
muchos casos, cosa que no es as en las clases adaptadoras.
- 44 -
Algunos entienden este principio como la restriccin de no agregar ningn atributo ni mtodo, pero s redefinicin.
En realidad, los constructores y destructores pueden tener el nombre que ms le guste al programador, pero hay muchas aplicaciones
que cuando utilizan clases y componentes Delphi buscan explcitamente un constructor Create y un destructor Destroy.
29
- 45 -
CARLOS FONTELA
respectivo. De todas maneras, todo esto es transparente al usuario, pues la notacin que se utiliza no es la de
punteros en Pascal sino la tradicional de objetos. Como es relativamente obvio, la liberacin de memoria se
hace invocando al destructor.
Este uso de referencias tiene la ventaja de la flexibilidad a costa de un mayor tiempo de ejecucin en
el momento de crear y destruir objetos 30. Otro inconveniente es el problema de los alias en asignaciones y
pasaje de parmetros, que se da usualmente al trabajar con punteros 31 , que se puede subsanar escribiendo un
mtodo que asigne un objeto a otro.
Adems, este modelo de memoria es el recomendado por el OMG (Object Management Group) para
garantizar la interoperabilidad de objetos distribuidos.
Como la notacin de punteros de Pascal no es necesaria cuando se trabaja con objetos, en Object
Pascal se pueden implementar estructuras de datos dinmicas sin usar punteros explcitos.
Cualquier objeto puede contener otros objetos, de modo tal que la composicin se hace de forma
natural. Lo nico que hay que recordar es que, en realidad, lo nico que se guarda en el objeto contenedor es
una referencia al objeto contenido.
La herencia se indica poniendo entre parntesis el nombre de la clase ancestro inmediato, como a
continuacin:
type cElipse = class (cFigura)
{ declaraciones de atributos y mtodos }
end;
Desde un mtodo redefinido se puede llamar al mtodo que se est redefiniendo en la clase ancestro,
usando la palabra inherited , como ya se mostr al hablar de constructores y destructores.
Las interfaces se parecen a las clases, pero sus mtodos no tienen implementacin. A continuacin se
muestra la declaracin de una interfaz:
type iPersistente = interface
procedure Guardar;
function Recuperar : TObject;
end;
Para implementar una interfaz en una clase hay que hacerlo poniendo a la interfaz como si fuera una
clase ancestro y ponindola a continuacin de su ancestro inmediato, como en:
type cTriangulo = class (cFigura, iPersistente)
{ declaraciones de atributos y mtodos }
end;
Se pueden declarar variables que sean instancias de interfaces, aunque cuando se las cree se deber
hacerlo llamando a un constructor que implemente esa interfaz pero que sea de una clase con implementacin.
Esto va a implicar el uso abundante de moldeo hacia abajo, que se analiza luego.
As como hay una clase TObject, ancestro de todas las clases por defecto, existe tambin IInterface,
ancestro de todas las interfaces32 .
Composicin y herencia simple y mltiple en C++
El modelo de memoria de C++ para objetos es el mismo que en el resto de los tipos. Es decir,
30
- 46 -
mientras no se diga otra cosa, todos los datos se alojan en memoria esttica. Si se quiere utilizar memoria
dinmica se deben utilizar punteros.
La ventaja inocultable de este modelo es la velocidad de ejecucin, aunque se puede desperdiciar
espacio de memoria por la necesidad de asignarla antes de conocer cunta se necesitar exactamente.
Como contra adicional, este modelo de memoria no cumple con las recomendaciones del OMG
(Object Management Group) para garantizar la interoperabilidad de objetos distribuidos.
Cualquier objeto puede contener otros objetos, de modo tal que la composicin se hace de forma
natural. Hay que recordar que, en estos casos, salvo que se utilicen punteros, se guarda el objeto contenido
dentro del objeto contenedor.
La herencia se indica poniendo a continuacin dos puntos y el nombre de la clase ancestro inmediato,
como a continuacin:
class cElipse : public cFigura
{
// declaraciones de atributos y mtodos
}
Ntese que delante de la clase ancestro se puede poner un atributo de visibilidad: pblico, privado o
protegido. Si se hace una herencia privada significa que los atributos y mtodos de la clase ancestro sern
privados en la clase que se define, de modo que no podrn usarse en sus derivadas. Si, en cambio, se declara a
la herencia como protegida, los atributos y mtodos pblicos y protegidos de la clase ancestro sern protegidos
en la clase que se est declarando. Si, finalmente, se declara una herencia pblica, todos los atributos y mtodos
de la clase ancestro mantienen sus reglas de visibilidad en la clase actual.
Desde un mtodo redefinido se puede llamar al mtodo que se est redefiniendo en alguna clase
ancestro, usando el operador :: de resolucin de mbito.
Como ya dijimos, C++ define un constructor y un destructor por omisin para cada clase. Adems,
stos invocan automticamente al constructor o destructor, respectivamente, de su clase ancestro inmediata.
Para definir otros, se hace con el nombre de clase para los constructores y con el nombre de clase precedido de
~ para los destructores.
Si una clase hereda de ms de un ancestro inmediato (herencia mltip le), la invocacin de sus
constructores se hace segn el orden en que fue declarada la herencia (de izquierda a derecha) y la de sus
destructores en forma inversa a ste (de derecha a izquierda).
La herencia mltiple se hace poniendo ms de una clase en la lista de los ancestros, como:
class cTRectIsos : public cTRectangulo, public cTIsosceles
{
// declaraciones de atributos y mtodos
}
C++ permite definir clases base virtuales para resolver ambigedades en herencia mltiple. Lo que
logran estas clases base virtuales es que exista slo una copia de los atributos y mtodos de sus ancestros en sus
clases derivadas. Si no se declaran virtuales, habr una copia de los mtodos y atributos por cada clase base que
los tenga, aun teniendo el mismo nombre, debiendo resolverse de alguna forma la ambigedad.
Como ya dijimos, las interfaces no estn soportadas en C++, pero pueden simularse con mtodos
abstractos (los veremos en el prximo captulo) y herencia mltiple.
Composicin, herencia e interfaces en Java
En Java, todas las clases que no se declaran con un ancestro inmediato tienen una clase ancestro por
omisin llamada Object.
El modelo de memoria de Object es tal que los objetos instancias de esta clase y sus descendientes se
- 47 -
CARLOS FONTELA
alojan en memoria dinmica. La declaracin de un objeto slo reserva lugar para una referencia (una especie de
puntero), pero no para el objeto en s. Por eso, es en tiempo de ejecucin que se invoca al constructor que,
adems de lo que indique su cdigo, asignar un lugar de memoria para el objeto respectivo. De todas maneras,
todo esto es transparente al usuario, pues la notacin que se utiliza es la tradicional de objetos. La liberacin de
memoria no se hace mediante destructores, que en Java no existen, sino que la hace el recolector automtico de
basura.
Este uso de referencias tiene la ventaja de la flexibilidad a costa de un mayor tiempo de ejecucin en
el momento de crear objetos 33. Otro inconveniente es el problema de los alias en asignaciones y pasaje de
parmetros, que se da usualmente al trabajar con punteros 34 , que se puede subsanar escribiendo un mtodo que
asigne un objeto a otro.
Adems, este modelo de memoria es el recomendado por el OMG (Object Management Group) para
garantizar la interoperabilidad de objetos distribuidos.
Un problema del recolector de basura, es que nunca se puede asegurar cundo va a ser invocado 35 ,
por lo que no se pueden determinar tiempos iguales para procesos iguales ni establecer cundo se realizan las
tareas que se ejecutan al ponerse en funcionamiento el recolector de basura. En Java existe un mtodo finalize()
que se puede declarar para cada clase, para ser invocado cuando se active el recolector de basura. Pero no hay
forma de saber en qu momento ocurrir esto, y en un programa chico o corriendo en una mquina de mucha
memoria tal vez no ocurra nunca. Por lo tanto, finalize() no es tampoco un destructor, y no se usa demasiado.
Dentro de finalize() se debera invocar al finalize() de la clase ancestro de la misma forma que se describe para
destructores en C++ y Object Pascal. Por lo tanto, si se desean ejecutar algunas acciones antes de dejar de usar
un objeto, el programador se debe preocupar de insertarlas en el cdigo apropiadamente.
Se puede alegar que la recoleccin de basura es lenta, pero al decir esto estaramos olvidando que no
siempre se la hace, sino slo cuando es necesaria, con lo cual puede incluso llegar a mejorar el desempeo de la
aplicacin.
Cualquier objeto puede contener otros objetos, de modo tal que la composicin se hace de forma
natural. Lo nico que hay que recordar es que, en realidad, lo nico que se guarda en el objeto contenedor es
una referencia al objeto contenido.
La herencia se indica poniendo a continuacin la palabra extends y el nombre de la clase ancestro
inmediato, como a continuacin:
class cElipse extends cFigura {
// declaraciones de atributos y mtodos
}
Desde un mtodo redefinido se puede llamar al mtodo que se est redefiniendo en la clase ancestro,
usando la referencia a super:
public void M (int X, int Y) {
super.M(X);
// llama al M de la clase ancestro
System.out.println(Y);
}
Como ya dijimos, Java define un constructor por omisin para cada clase. Adems, ste invoca
33
- 48 -
automticamente al constructor de su clase ancestro inmediata. Para definir otros, se hace con el nombre de
clase.
Se puede declarar que un determinado mtodo no va a poder ser redefinido anteponindole la palabra
final.
Se puede declarar una clase como final para que no se puedan construir clases derivadas de la misma.
De todas maneras, es preferible evitar declarar mtodos y clases como final porque impiden reutilizacin y
extensin.
Las interfaces se parecen a las clases, pero sus mtodos no tienen implementacin. A continuacin se
muestra la declaracin de una interfaz:
public interface iPersistente {
public void Guardar();
public Object Recuperar();
}
Para implementar una interfaz en una clase hay que especificarlo a continuacin de la clase ancestro
inmediato, colocando la palabra implements:
class cTriangulo extends cFigura implements iPersistente {
// declaraciones de atributos y mtodos
}
Se pueden declarar variables que sean instancias de interfaces, aunque cuando se las cree se deber
hacerlo llamando a un constructor que implemente esa interfaz pero que sea de una clase con implementacin.
Esto va a implicar el uso abundante de moldeo hacia abajo, que se analiza luego.
- 49 -
CARLOS FONTELA
- 50 -
Hasta que empec la escritura de esta obra, lo que hoy llamo redefinicin lo haba llamado sobrecarga. Ocurre que la mayor parte
de la literatura utiliza sobrecarga para referirse a la propiedad, en ciertos lenguajes, de que distintos subprogramas pueden tener el
mismo nombre, incluso fuera de la POO. En ingls se los llama overriding y overloading, aunque incluso hay diferencias sobre lo
que overriding quiere decir.
37
La notacin es de Java o C++, pero ocurre lo mismo en Object Pascal.
- 51 -
CARLOS FONTELA
Como principios generales, se debe utilizar redefinicin cuando en una subclase pueda realizarse una
implementacin ms eficiente o ms completa que la de la clase ancestro. En consecuencia, la redefinicin:
?? Ser obligatoria, cuando el comportamiento de una clase descendiente sea diferente del de la
ancestro. Por ejemplo, si hay un mtodo que se llama ImprimirAtributos que imprime todos
los atributos de un objeto, ste deber redefinirse en las clases derivadas si queremos que
funcione correctamente.
?? Ser optativa, cuando por razones de eficiencia o claridad queramos modificar la
implementacin del mtodo del ancestro. Por ejemplo, el mtodo para calcular la longitud de
una elipse probablemente funcione bien con crculos, pero su implementacin es mucho ms
complicada.
?? Debe preservar la semntica de la definicin del mtodo en el ancestro. Es decir que la tarea
debe ser la misma, aunque se haga de diferente forma.
Tengamos en cuanta que la redefinicin se introdujo para cambiar comportamiento de una clase
descendiente respecto del ancestro, no para introducir cambios en la estructura. Por lo tanto, se pueden redefinir
mtodos, pero no atributos.
Los mtodos privados no pueden ser redefinidos. Si se lo intenta, en realidad se estar definiendo un
nuevo mtodo. Al fin y al cabo, la redefinicin slo tiene sentido si el mtodo es parte de la interfaz del
ancestro, y los mtodos privados no son parte de la interfaz.
Sobrecarga y redefinicin
La sobrecarga es un concepto que poco tiene que ver con POO. Exista previamente en lenguajes
como Pascal en la biblioteca estndar, como con los subprogramas Read, Write y dems. En Ada, desde sus
primeras versiones, se poda incluso definir por el programador. Adems, no tiene nada que ver con la
vinculacin dinmica o el polimorfismo.
Sin embargo, hay algunas interacciones con el concepto de redefinicin que nos interesa analizar.
La relacin ms conflictiva de la sobrecarga y la redefinicin se da cuando redefinimos un mtodo en
una clase descendiente con un encabezado distinto al del mtodo ancestro. En este caso, se efectuar una
redefinicin, en el sentido de ocultar la definicin hecha en la clase base? o ms bien se la tratar de un nuevo
mtodo que sobrecargar a los de la clase ancestro? La respuesta a esta pregunta depende de cada lenguaje.
En Object Pascal, como dijimos, se debe poner la palabra overload en todos los mtodos que se
sobrecargan a un mismo nivel. Del mismo modo, si se quiere sobrecargar un mtodo de una clase ancestro se
debe indicar claramente con la palabra overload en la descendiente. Si no se hiciera as, se toma el nuevo
mtodo como una redefinicin, y esto impide el acceso al mtodo de la clase ancestro desde la descendiente.
En Java y en C++, cuando se utilizan encabezados diferentes en una clase ancestro y una
descendiente, esto no redefine el mtodo de la ancestro sino que provoca una sobrecarga. Como corolario, los
mtodos que no se redefinan con el mismo encabezado de la clase base, siguen estando disponibles para la
clase descendiente.
Objetos polimorfos o conversin de tipo automtica
Cuando un programador de un lenguaje procedimental con tipos escribe un subprograma sabe
siempre exactamente de qu tipo van a ser los argumentos que se le pasen.
Pero en POO se puede pasar un argumento cuya clase sea derivada de la clase del parmetro38. Por
38
Si bien algunos autores tratan a los trminos argumento y parmetro como sinnimos, en mis obras tienen un significado
diferente. Argumento significa lo mismo que parmetro actual y parmetro es lo mismo que parmetro formal. Dicho en otros trminos,
- 52 -
Ntese que P admite un argumento cuyo tipo es distinto del del parmetro.
Por lo tanto, cuando utilizamos POO, el programador del mtodo desconoce la clase del objeto que
va a recibir como argumento, y ni siquiera el compilador la conoce. Se dice que dicho programador y el
compilador estn trabajando con un objeto polimorfo , del cual slo se conocen los atributos y comportamiento
que hered de la clase del parmetro. Esto permite el procesamiento de objetos cuya clase es desconocida en
tiempo de compilacin.
Esto no es una desventaja ; es una consecuencia positiva de la herencia que provoca importantes
ahorros de tiempo por reutilizacin del cdigo, ya que no es necesario repetir un mismo mtodo para
parmetros de clases distintas.
Hay quien no considera polimorfismo al uso de objetos polimorfos, pues en este caso el mtodo
invocado es siempre el mismo. Sin embargo, para quien establece que siempre que hay vinculacin tarda entre
parmetros y argumentos hay polimorfismo, ste sera un caso.
Mtodos virtuales
Supongamos que en la jerarqua de clases de figuras que ya hemos mencionado, definimos mtodos
Mover , Borrar y NuevasCoordenadas en la clase cFigura , mientras que definimos el mtodo Dibujar en
cFigura, pero lo redefinimos en las clases descendientes:
cFigura
+Dibujar()
+Borrar()
+NuevasCoordenadas()
+Mover()
cElipse
+Dibujar()
cCuadrilatero
+Dibujar()
cTriangulo
+Dibujar()
cOtros
+Dibujar()
- 53 -
CARLOS FONTELA
Borrar;
NuevasCoordenadas(X,Y);
Dibujar;
end;
Cmo se hace para que cada vez se llame al mtodo Dibujar correcto?
Precisamente, si se hace vinculacin tarda, podemos lograr que el Dibujar que se invoque
corresponda siempre al objeto para el cual se invoc Mover. Es decir:
UnaElipse.Mover;
{ llamar al Mover de cFigura pero con el Dibujar de cElipse }
UnCuadrilatero.Mover;
{ llamar al Mover de cFigura con el Dibujar de cCuadrilatero }
Veamos otro ejemplo en Object Pascal (donde cVectorFiguras es un vector de elementos cFigura):
procedure cVectorFiguras.DibujarTodas;
var I : Word;
begin
for I := 1 to CantFiguras do
Vector[I].Dibujar;
end;
De nuevo, la vinculacin tarda garantiza que el Dibujar invocado es el que corresponde a la clase
del elemento del vector (que podr ser de cualquier descendiente de cFigura).
Los mtodos que son vinculados dinmicamente o tardamente se denominan mtodos virtuales. En
los ejemplos anteriores, Dibujar debera ser un mtodo virtual para garantizar la vinculacin tarda.
Hay lenguajes, como Smalltalk y Java, que toman la vinculacin tarda por omisin, por lo cual todos
los mtodos son virtuales. Otros, como C++ y Object Pascal, requieren que el programador especifique el
mtodo como virtual explcitamente.
Las reglas generales para el uso de mtodos virtuales son que un mtodo debe ser virtual en alguna
de estas circunstancias:
?? Cuando est redefinido y es llamado desde un mtodo no redefinido en la clase ancestro.
?? Cuando est redefinido y es invocado desde un mtodo de una clase compuesta con algn
objeto de una clase ancestro.
Los mtodos virtuales suelen consumir ms recursos (memoria y/o tiempo de eje cucin) que los
estticos. Sin embargo, en orden a la reutilizacin, ante la duda, un mtodo debe ser virtual.
Constructores y destructores virtuales
Los destructores casi siempre son virtuales. De ese modo, si se escribe un mtodo en una clase base
que in voca a un destructor virtual, se puede asegurar que siempre llama al destructor de la clase correcta.
Por ejemplo, en Object Pascal, el destructor Destroy es virtual, de all que en todos nuestros ejemplos
en ese lenguaje hayamos puesto la directiva override en los Destroy redefinidos. A su vez, como Destroy no
hace una verificacin de que la referencia est sin apuntar a nada, existe un mtodo que llama al Destroy de
TObject que se denomina Free, que hace esta verificacin. A su vez, tambin en TObject existe el mtodo
FreeAndNil, que hace lo mismo que Free pero poniendo la referencia en nil, de modo que es el ms seguro de
los tres. La idea, entonces, es redefinir Destroy en todas las clases en que sea necesario, pero invocar siempre a
- 54 -
FreeAndNil, que por estar definido en TObject se pude utilizar en todas las clases sin redefinirlo, y estamos
asimismo seguros de que invoca al Destroy de nuestra clase por ser ste virtual.
En C++ es importante que los destructores sean virtuales por las mismas razones.
Los constructores, en cambio, no suelen ser virtuales. Para empezar, un constructor se usa para crear
un objeto, parece un poco raro crear uno sin saber su clase de antemano. De hecho, ni en C++ ni en Object
Pascal se pueden declarar constructores virtuales, salvo haciendo algunos artificios.
De todas maneras, si se desease una funcionalidad polimrfica en un constructor, se puede escribir un
mtodo virtual que llame a un constructor.
En Object Pascal debe hacerse:
type cClaseAInstanciar = class of cClase1;
function CrearObjeto (Tipo: cClaseAInstanciar): cClase1;
begin
Result:= Tipo.Create(....);
end;
Grados de polimorfismo
Ahora que hemos visto varios comportamientos polimrficos en POO, volvamos al anlisis del
propio polimorfismo. Debe entenderse que lo fundamental del polimorfismo es que rompe con la tpica
vinculacin temprana de los lenguajes compilados tradicionales, que vinculan en tiempo de compilacin la
llamada a un subprograma con la direccin absoluta de ste.
El primer paso hacia el polimorfismo lo dimos con la herencia y la redefinicin. De hecho, un
mtodo acta, no slo sobre objetos de la clase para la cual fue declarado, sino para cualquier clase
descendiente de aqulla. Aqu hay polimorfismo al nivel del parmetro invisible Self/this.
Los objetos polimorfos son un segundo grado de polimorfismo, que amplan el campo de la herencia
a la relacin parmetro - argumento. En este caso hay polimorfismo al nivel de todos los parmetros del
mtodo.
Finalmente, con los mtodos virtuales y la vinculacin tarda, se ha dado un paso ms: se ha logrado
que se demore hasta el tiempo de ejecucin la decisin sobre qu mtodos invocar dentro del cdigo de otro
mtodo 39 . Es el objeto quien realiza la accin en una manera apropiada a s mismo (y no a la clase donde est
declarado el mtodo). Hay polimorfismo a nivel de cdigo del mtodo.
Es obvio que el polimorfismo nos presta un especial servicio para la reutilizacin y la extensibilidad.
En programacin tradicional Pascal deberamos escribir un subprograma para cada tipo de dato, repitiendo
tediosamente segmentos de cdigo. Con la vinculacin tarda, un mtodo escrito para una clase podr seguir
funcionando bien aun cuando se agreguen clases derivadas que no estaban siquiera previstas por el
programador del ancestro.
En tiempo de compilacin lo nico que se hace es una verificacin de tipos y cantidad de argumentos y parmetros y valor devuelto.
- 55 -
CARLOS FONTELA
Estas clases se denominan clases abstractas , y tienen como nica finalidad la declaracin de
atributos y mtodos comunes que luego se utilizarn en las clases descendientes. Dicho formalmente, se
construyen para generalizar estructura y comportamiento comunes de varias clases descendientes. En otros
casos ni siquiera poseen atributos o mtodos, sin o que slo se las define para luego declarar subclases que por
algn motivo se las desea tratar como de la misma familia.
En el ejemplo anterior, aunque cFigura no tenga instancias, nos sirvi para declarar el mtodo
Mover, de modo que las clases descendie ntes lo pudieran usar.
Las interfaces son un caso especial de clase abstracta. Por eso, si una clase abstracta no necesita
implementar mtodos o atributos, habra que analizar si no debiera ser una interfaz.
Mtodos abstractos
Hay otro aspecto parecido al de las clases abstractas. Lo explicaremos con el ejemplo que estamos
analizando.
El mtodo Dibujar de la clase cFigura nunca ser invocado, pues no hay instancias de cFigura y est
redefinido en todas las clases descendientes. Una primera solucin, incorrecta, sera suprimirlo y dejarlo
solamente en las clases hijas. Sin embargo, si hacemos eso, nos encontraremos con que el mtodo Mover de
cFigura no podra ser compilado, ya que en su cdigo fuente invoca a Dibujar, que no est declarado en la
clase cFigura ni en ninguno de sus ancestros. Lo mismo ocurrira con el DibujarTodas de cVectorFiguras que
escribimos ms arriba.
La mejor solucin, entonces, sera escribir el mtodo Dibujar en cFigura, convirtindolo en mtodo
abstracto, esto es, en un mtodo que no puede ser invocado por ninguna instancia de la clase cFigura , aunque
existiera, pero que sirve para que se pueda escribir el mtodo Mover de cFigura tal como se hizo.
Como consecuencia de lo dicho anteriormente, los mtodos abstractos:
?? Siempre deben ser redefinidos, y en algn nivel inferior de la jerarqua deben dejar de ser
abstractos, ya que no se los puede invocar directamente.
?? Deben ser virtuales.
?? No tienen cdigo fuente ejecutable.
?? Son fundamentales en jerarquas de una complejidad mediana.
?? Son necesarios cuando en la clase ancestro hay que utilizarlos en la implementacin de otro
mtodo (como explicamos en el ejemplo con cFigura y cVectorFiguras).
Ntese en el ejemplo del vector de figuras que el DibujarTodas de cVectorFiguras no debera ser
abstracto, ya que l mismo debera ser invocado directamente.
Los mtodos de las interfaces son, por definicin, todos abstractos.
Clases utilitarias y clases sin estado
Las clases utilitarias son clases abstractas que sirven para definir constantes, mtodos que no
dependen de una clase en particular y dems. La idea es que para cada aplicacin, mdulo o familia de
aplicaciones haya una clase en la cual definimos todos los objetos, constantes y clases comunes. Este es un
concepto fundamental en lenguajes como Java, en los cuales todo identificador debe definirse dentro de una
clase.
As haremos en los ejercicios que sigan en Java para definir constantes o mtodos de clase.
Un caso extremo son las clases sin estado, como las que definen familias de funciones en Java. Un
ejemplo es la clase Math , que contiene mtodos de uso matemtico.
- 56 -
transforma el contenido de la variable x en un char y luego lo asigna a c. Esto podra ser factible, por
ejemplo, si x es un entero y c un char.
Lo mismo puede aplicarse en Pascal, aunque en este lenguaje se hace un muy escaso uso de estas
posibilidades. La lnea que sigue transforma un entero en carcter en Pascal:
c = char(x);
Esta propiedad de la transformacin de tipos se suele denominar moldeo, por el uso del verbo ingls
to cast que se aplica en estos casos en el sentido de tomar un papel en una actuacin. Hay tambin quienes lo
denominan usan el barbarismo casteo.
Sin embargo, el concepto de transformacin de tipos y, en forma ms general, de uso de la
informacin sobre tipos en tiempo de ejecucin tiene un valor importante en el contexto de POO cuando se usa
en conjunto con el polimorfismo y la vinculacin tarda en general.
Moldeo automtico y moldeo explcito
Cuando se trabaja en POO, hay una transformacin de tipos que se hace en forma automtica, que se
da cada vez que se usa una instancia de una clase derivada donde se puede utilizar una instancia de la clase
base. En casi todos los lenguajes de POO est permitido lo que sigue 40 :
UnaFigura := UnaElipse;
ya que cElipse es una clase descendiente de cFigura. Tambin vimos el concepto de objeto
polimorfo, por el cual si tenemos un subprograma con el siguiente encabezado:
procedure P (X:cFigura);
En estos casos decimos que se est haciendo un moldeo automtico o implcito, pues no hace falta
especificarle al lenguaje que se quiere hacer una transformacin de tipos. Tambin se lo llama moldeo hacia
arriba, porque se est transformando el valor llevndolo hacia una clase que est ms arriba que la propia en la
jerarqua.
Ntese que en cuanto a los atributos y mtodos, en estos casos el objeto de la clase ancestro (el que
figura a la izquierda en la asignacin o es el parmetro del mtodo) slo podr luego usar los que tiene
declarados en su propia declaracin de clase.
Sin embargo, hay casos en los cuales puede ser necesario hacer un moldeo hacia abajo, lo cual de
ninguna manera es automtico. Por ejemplo, si necesito asignar a una variable de clase cElipse un valor de tipo
cFigura, no es una operacin que se haga en forma automtica, y debe hacerse en forma explcita, como
muestra el siguiente fragmento de cdigo Pascal:
UnaElipse := cElipse(UnaFigura);
40
- 57 -
CARLOS FONTELA
Este tipo de moldeo es ms peligroso, pues podran quedar valores de atributos no inicializados. Por
eso, la mayor parte de los lenguajes tienen algn mecanismo para hacer una verificacin en tiempo de ejecucin
de modo que esto no ocurra.
Para qu puede usarse el moldeo explcito? Un caso interesante es el de tener que acceder a un
atributo o mtodo que estuviera declarado en la clase descendiente desde un objeto en la ancestro. El siguiente
ejemplo ilustra esto:
UnaFigura := UnaElipse;
(UnaFigura as cElipse).Dibujar; { usa el Dibujar de cElipse }
Ntese que esto no sera correcto sin la primera lnea, pues el mtodo Dibujar de cElipse podra
intentar acceder a atributos que estn declarados en cElipse y no en cFigura, lo cual podra conducir a errores
impredecibles. Por eso, el moldeo hacia abajo debe hacerse siempre sobre objetos que en realidad sean de la
clase descendiente, aunque por alguna razn estn asignados a una variable de la clase ancestro.
En este punto hace falta una aclaracin. Para que lo anterior sea estrictamente cierto deberamos
garantizar que la lnea
UnaFigura := UnaElipse;
permita guardar la informacin completa de la elipse sin perder parte de su estado. Esto es cierto
solamente en los lenguajes en los cuales el modelo de memoria de objetos trabaja sobre referencias, como Java
y Object Pascal. Si, en cambio, el modelo de memoria implementa los objetos sobre memoria esttica, la nica
forma de garantizar esto es usando punteros, como muestra el siguiente fragmento de cdigo C++:
PUnaFigura = *UnaElipse; // es una asignacin de punteros
(cElipse)(*PUnaFigura).Dibujar; // usa el Dibujar de cElipse
No obstante, sigue pareciendo un uso raro. Por qu hara falta hacer la asignacin de la primera
lnea, en vez de trabajar directamente con una variable de tipo cElipse? La respuesta ms convincente la
encontraremos en el siguiente tem.
Datos estructurados con elementos de varios tipos
Hay un caso particular de objeto polimorfo que es el elemento de una coleccin de referencias. En
los lenguajes que implementan los objetos como referencias, si se declara un arreglo cuyos elementos son de
tipo cFigura, se les podr asignar elementos de diferentes clases descendientes de cFigura.
Esto se debe a la posibilidad de asignar a un objeto una instancia de una clase descendiente, que hace
posible construir datos estructurados cuyos elementos no sean todos del mismo tipo, sino que pertenezcan a una
familia de clases.
Por ejemplo, las declaraciones siguientes en Object Pascal:
type ArregloLibre = array [1..3] of cFigura;
var V : ArregloLibre;
Aun cuando el lenguaje, como C++, implemente los objetos en memoria esttica, siempre se puede
declarar una referencia a un objeto, por lo que un arreglo de punteros a elementos cFigura podr llenarse con
punteros a cElipse, cCuadrilatero y dems.
En lenguajes en los cuales hay una clase por defecto que es ancestro ltimo de toda la jerarqua,
como Java y Object Pascal, este concepto puede ser llevado an ms all, pues el uso de estructuras cuyos
elementos son Object (en Java) o TObject (en Object Pascal), nos permite trabajar con estructuras totalmente
- 58 -
genricas. Java lo aprovecha especialmente en las bibliotecas de clases que provee al programador. El siguiente
ejemplo lo muestra:
List v = new ArrayList(); // un arreglo de elementos Object
v.add(new cElipse(0,0)); // le agrego un elemento de clase cElipse
v.add(new cCirculo(0,0)); // le agrego un elemento de clase cCirculo
v.add(new Integer(-8));
// le agrego un elemento de clase Integer
v.add(new ArrayList(1)); // le agrego un elemento de clase ArrayList
En estos casos, el moldeo hacia abajo puede ser ms necesario. Verbigracia, en el ejemplo anterior,
podra querer invocar a un mtodo propio de la clase cElipse para el primer elemento del arreglo:
(v.get(0)).Dibujar(); // incorrecto
v.get(0) obtiene el primer elemento del arreglo. Pero como es una elipse, pretendo dibujarla. Ahora
bien, v es un arreglo de elementos Object, por lo cual no puedo invocar con un elemento del arreglo el mtodo
Dibujar, propio de una clase descendiente. Por lo tanto, la lnea de ms arriba debi escribirse, moldeando el
elemento a cElipse:
((cElipse)(v.get(0))).Dibujar();
Ahora bien, para poder hacer esto habra que estar seguro de que el tipo del primer elemento del
arreglo es una elipse, lo cual podra no saberse si el bloque en el cual se est escribiendo la lnea anterior no
est en el mismo mbito en el cual se llen el arreglo v.
Para resolver este problema necesitamos poder evaluar el tipo del dato de cada elemento en tiempo
de ejecucin. Esto lo veremos en el tem que sigue.
Informacin de tipos en tiempo de ejecucin
En muchos lenguajes se puede preguntar por el tipo de datos de un objeto en tiempo de ejecucin,
aunque la sintaxis es diferente en cada caso. Pero lo que importa es que esto nos da un grado de seguridad
mayor al hacer el moldeo.
Por ejemplo, el moldeo a cElipse que mostramos en el tem anterior debera haberse escrito:
if (v.get(0) instanceof cElipse)
(cElipse)(v.get(0)).Dibujar();
La primera lnea chequea que el primer elemento de v sea una instancia de la clase cElipse antes de
efectuar el agregado de un elemento en la segunda lnea.
Es fcil abusar de esto. Por ejemplo, supongamos que se quiere escribir en Object Pascal un mtodo
que dibuje todos los elementos de un arreglo de elementos cFigura. Una primera idea podra ser:
procedure cVectorFiguras.DibujarTodas;
var I : Word;
begin
for I := 1 to CantFiguras do
if Vector[I] is cElipse
then (Vector[I] as cElipse).Dibujar
else if Vector[I] is cCuadrilatero
then (Vector[I] as cCuadrilatero).Dibujar
else if Vector[I] is cTriangulo
then (Vector[I] as cTriangulo).Dibujar
else (Vector[I] as cOtros).Dibujar
end;
- 59 -
CARLOS FONTELA
Esta solucin, adems de ser bastante poco elegante y extensa, tiene el inconveniente de que limita
seriamente la extensibilidad. Qu ocurrira si quien adquiere estas clases desea agregar una clase cPentagono
descendiente de cFigura? Podra pensarse que debera modificar cVectorFiguras.DibujarTodas. Pero esto sera
un grueso error, pues ese mtodo es parte de la biblioteca que adquiri, y presumimos que se encuentra
compilada.
La respuesta es la tradicional vinculacin tarda. Si el mtodo Dibujar es virtual, el mtodo
DibujarTodas de arriba se escribir:
procedure cVectorFiguras.DibujarTodas;
var I : Word;
begin
for I := 1 to CantFiguras do
Vector[I].Dibujar;
end;
Ntese que para cada Vector[I] se est utilizando el mtodo Dibujar que corresponda a la clase del
elemento. De esta manera no se comprometer la extensibilidad y la solucin es bastante ms limpia.
Las clases abstractas no estn implementadas en Object Pascal. Es ms, ni siquiera es como en otros
lenguajes en que una clase con mtodos abstractos no puede instanciarse42 .
Los mtodos abstractos s se pueden implementar con el agregado de la directiva abstract:
procedure Dibujar; virtual; abstract;
El moldeo de clases se puede hacer como se haca para los tipos tradicionales en Pascal, como en:
cElipse(UnaFigura).Dibujar;
Sin embargo, en este caso no se va a verificar que el objeto almacenado en UnaFigura sea realmente
41
virtual significa que la implementacin va a optimizar los tiempos de ejecucin, mientras que con dynamic buscar minimizar el uso
de memoria. Por lo dems, la semntica es la misma.
42
Aunque el compilador Delphi lanza una advertencia si se la instancia.
- 60 -
una elipse. Para evitar este inconveniente se puede utilizar el operador as:
(UnaFigura as cElipse).Dibujar;
Object Pascal brinda algunas herramientas para trabajar con informacin sobre tipos de objetos en
tiempo de ejecucin. Por ejemplo, implementa el concepto de reflejado, que permite conocer tipos de objetos o
de sus ancestros, que no tienen significado en tiempo de compilacin, por ejemplo, porque las clases se reciben
en tiempo de ejecucin a travs de una red. Estos son conceptos muy importantes para permitir el uso de
componentes distribuidos, invocacin remota de mtodos y otros.
Polimorfismo y otros aspectos en C++
En C++ la redefinicin se hace simplemente reescribiendo el mtodo en cuestin en la clase
derivada. Sin embargo, para evitar la sobrecarga y efectivamente lograr el ocultamiento del mtodo de la clase
ancestro, el mtodo deber mantener la misma cantidad y el mismo tipo de parmetros del de la clase ancestro.
Los mtodos virtuales se deben declarar explcitamente, colocando virtual antes del nombre en la
declaracin del mtodo, como en el ejemplo:
virtual void dibujar ();
Esto se hace nicamente en la clase ms alta de la jerarqua donde aparezca el mtodo. En las clases
descendientes el mtodo ser virtual por definicin.
Los mtodos abstractos son llamados funciones virtuales puras. Como su nombre lo indica, un
mtodo abstracto es siempre virtual. Se declaran como en el ejemplo:
virtual void dibujar () = 0;
y luego no se implementan.
Las clases abstractas son aquellas que contienen mtodos abstractos. Como corolario, no hay forma
de definir clases abstractas sin declarar un mtodo abstracto en ellas, ni hay forma de declarar mtodos
abstractos en clases que no sean abstrac tas.
El moldeo de clases se puede hacer como en C:
((cElipse)UnaFigura).Dibujar();
Pero as no se verifica que el objeto almacenado en UnaFigura sea realmente una elipse. Si se quiere
hacer un moldeo ms seguro se puede obtener informacin sobre la clase del objeto utilizando el operador
typeid, que funciona cuando comparamos clases de la misma jerarqua y las clases poseen algn mtodo virtual.
Todo esto es muy restrictivo.
Afortunadamente, hay otro mecanismo, mediante el moldeo dinmico, que adems es seguro y
funciona independientemente de la jerarqua. La transformacin anterior se escribira as:
if (dynamic_cast<cElipse*>(UnaFigura) != null)
dynamic_cast<cElipse*>(UnaFigura).Dibujar();
Ntese que la misma funcin dynamic_cast sirve para hacer el moldeo (segunda lnea) como para
verificar que la transformacin sea vlida, ya que si no lo es la funcin devuelve null.
Lamentablemente, el moldeo dinmico es ms ineficiente que el uso de typeid, por lo que muchos
programadores se inclinan por esta forma me nos segura.
- 61 -
CARLOS FONTELA
y no se implementan.
Si una clase contiene uno o ms mtodos abstractos ser abstracta.
Pero las clases abstractas tambin pueden definirse sin mtodos abstractos, simplemente
anteponindoles la palabra abstract. Esto tiene bastante sentido en el caso de que se cree una clase solamente
para agrupar otras o para generalizar estructura y comportamiento no abstracto. Vale decir, no siempre tengo
mtodos abstractos en una clase abstracta. El ejemplo que sigue muestra una clase abstracta:
abstract class cFigura {
// declaraciones de atributos y mtodos
}
Cualquier clase que implemente una interfaz debera implementar todos sus mtodos para dejar de
ser abstracta.
El moldeo de clases se puede hacer como en C:
(cElipse)UnaFigura.Dibujar();
La diferencia fundamental es que Java hace una verificacin que asegure que el objeto almacenado
en UnaFigura sea realmente una elipse.
La informacin sobre el tipo se puede realizar con el operador instanceof:
if (UnaFigura instanceof cElipse)
(cElipse)UnaFigura.Dibujar();
Una forma ms precisa de hacer esto ltimo 43 (aunque parezca menos clara) es:
if UnaFigura.getClass() == cElipse.class
(cElipse)UnaFigura.Dibujar();
Java brinda notables herramientas para trabajar con informacin sobre tipos de objetos en tiempo de
ejecucin. Por ejemplo, existen mtodos que permiten conocer los mtodos y atributos que tiene una
determinada clase, as como su clase ancestro y las interfaces que implementa. Incluso implementa el concepto
de reflejado, que permite conocer tipos de objetos que no tienen significado en tiempo de compilacin, por
ejemplo, porque las clases se reciben en tiempo de ejecucin a travs de una red. Estos son conceptos muy
importantes para permitir el uso de componentes distribuidos, invocacin remota de mtodos y otros.
43
Usando instanceof me aseguro de que UnaFigura es de clase cElipse o algn descendiente. Usando getClass y el atributo class puedo
asegurar que UnaFigura es exactamente de tipo cElipse.
- 62 -
- 63 -
CARLOS FONTELA
6. DOCUMENTANDO LA PROGRAMACIN
DOCUMENTACIN EN GENERAL
En general se habla mucho de la documentacin, pero no se la hace, no se le asigna presupuesto, no
se la mantiene y casi nunca est al da en los proyectos de desarrollo de software. Lo importante es la
disponibilidad de la documentacin que se necesita en el momento en que se la necesita.
Muchas veces se hace porque hay que hacerla y se escribe, con pocas ganas, largos textos, a la vez
que se est convencido de estar haciendo un trabajo intil. A veces se peca por exceso y otras por defecto.
Ocurre mucho en la Web y con productos RAD. En ocasiones se olvida que el mantenimiento tambin debe
llegar a la documentacin.
La documentacin para desarrolladores es aqulla que se utiliza para el propio desarrollo del
producto y, sobre todo, para su mantenimiento futuro. Se documenta para comunicar estructura y
comportamiento del sistema o de sus partes, para visualizar y controlar la arquitectura del sistema, para
comprender mejor el mismo y para controlar el riesgo, entre otras cosas. Obviamente, cuanto ms complejo es
el sistema, ms importante es la documentacin.
En este sentido, todas las fases de un desarrollo deben documentarse: requerimientos, anlisis,
diseo, programacin, pruebas, etc.. Una herramienta muy til en este sentido es una notacin estndar de
modelado, de modo que mediante ciertos diagramas se puedan comunicar ideas entre grupos de trabajo.
Hay decenas de notaciones, tanto estructuradas como orientadas a objetos. Un caso particular es el de
UML, que analizamos en otra obra. De todas maneras, los diagramas son muy tiles, pero siempre y cuando se
mantengan actualizados, por lo que ms vale calidad que cantidad.
La documentacin para desarrolladores a menudo es llamada modelo, pues es una simplificacin de
la realidad para comprender mejor el sistema como un todo.
Otro aspecto a tener en cuenta cuando se documenta o modela, es el del nivel de detalle. As como
cuando construimos planos de un edificio podemos hacer planos generales, de arquitectura, de instalaciones y
dems, tambin al documentar el software debemos cuidar el nivel de detalle y hacer diagramas diferentes en
funcin del usuario de la documentacin, concentrndonos en un aspecto a la vez.
De toda la documentacin para los desarrolladores, nos interesa especialmente en esta obra aquella
que se utiliza para documentar la programacin.
Para ver un anlisis ms completo de la documentacin, recomiendo el apunte que he escrito con
Pablo Surez, que se puede descargar de http://www.fi.uba.ar/materias/7507F/.
- 64 -
UML fue liberada al mercado y aprobada por el OMG (Object Management Group) en 1997.
UML es un lenguaje para la visualizacin, especificacin y documentacin de sistemas, por lo que
resulta independiente del modelo de proceso que se utilice para el desarrollo de los mismos (cascada, iterativo,
con prototipos, etc.). No es un mtodo sino una notacin, pues no especifica un proceso; lo que hace es
describir el resultado de alguna etapa del desarrollo de un sistema mediante una serie de diagramas.
La idea general de UML, as como de todas las notaciones de modelado orientadas a objetos, es
centrarse ms en los objetos que en los procesos o algoritmos.
Para describir los distintos aspectos de los sistemas, de modo de mostrar diferentes perspectivas44 ,
UML define 9 tipos de diagramas:
?? Diagramas de casos de uso: representan las funciones del sistema desde el punto de vista del
usuario, y algunos comportamientos fundame ntales del sistema, aunque sean invisibles para el
cliente normal.
?? Diagramas de clases: representan la estructura esttica en trminos de clases, interfaces y
colaboraciones, y las relaciones entre ellas.
?? Diagramas de componentes: representan componentes de software.
?? Diagramas de despliegue: son un caso particular de diagrama de clases que representan los
nodos de procesamiento en tiempo de ejecucin y los componentes que residen en ellos. Es el
diagrama ms apropiado para documentar los componentes de hardware, como procesadores y
dispositivos 45 .
?? Diagramas de objetos: modelan las instancias de los elementos de un diagrama de clases,
mostrando un conjunto de objetos en un momento concreto, con sus estados y relaciones.
?? Diagramas de secuencia: son una representacin temporal de los objetos y sus interacciones.
Junto con los diagramas de colaboracin se denominan diagramas de interaccin.
?? Diagramas de colaboracin: como los diagramas de secuencia, representan objetos e
interacciones, pero no tanto en forma temporal como privilegiando las relaciones entre los
distintos objetos. Junto con los diagramas de secuencia se denominan diagramas de
interaccin.
?? Diagramas de estados y transiciones: representa el comportamiento dinmico de un objeto en
trminos de estados, transiciones y eventos.
?? Diagramas de actividades: resalta el flujo de control entre objetos, pudindose aplicar a una
operacin o a todo un sistema. Es como un diagrama de interaccin, pero centrado en las
actividades y no en los objetos. Se suele aplicar tambin al modelado de las partes no
informticas de un sistema.
UML puede ser tan sencillo o complejo como se quiera. Los creadores de la notacin, si bien afirman
que todo lo que puede conceptualizarse puede ser representado con UML, para enfatizar la variedad del
lenguaje, tambin afirman que el 80% de la mayora de los sistemas se puede modelar con el 20% de las
construcciones de UML [18].
Tambin hay que tener en cuenta que es un lenguaje de comunicacin entre humanos 46 , por lo que no
tiene sentido elaborar diagramas muy complejos si nadie los va a entender.
44
La idea de las perspectivas diferentes puede verse con una analoga con la construccin de edificios. En estos casos, un plano de
arquitectura, una vista general, un plano de instalaciones sanitarias, un plano estructural, etc., todos se refieren al mismo edificio, pero
muestran aspectos diferentes, probablemente dirigidos a personas diferentes.
45
De todas maneras, slo son necesarios en sistemas en los que el hardware sea importante. No tiene sentido hacer un diagrama de
despliegue de un sistema que va a correr en una mquina aislada. Tal vez los mejores usos de los diagramas de despliegue se den en
sistemas empotrados, sistemas cliente-servidor y sistemas distribuidos.
46
Adems de poder usarse entre humanos y mquinas.
- 65 -
CARLOS FONTELA
En este curso vamos a estudiar superficialmente los diagramas de clases, de secuencia, de estados y
de actividades, para aplicarlos a los problemas que nos ocupan.
Otros diagramas muy importantes son los de casos de uso, pero se usan ms bien en las fases de
requerimientos y anlisis.
Diagramas de clases
Los diagramas de clases representan relaciones estticas entre clases, interfaces y colaboraciones47 .
Decimos estticas en el sentido de que son rela ciones que no cambian a travs del tiempo. Entre ellas, se
representan especialmente las asociaciones entre clases (que representan cmo se relaciona una clase con otra a
travs del uso) y las relaciones de herencia o implementacin de interfaces. Tambin se muestran los atributos,
la funcionalidad y las restricciones de acceso.
Una clase se representa con un rectngulo con tres divisiones: una para el nombre de la clase, otra
para atributos y otra para los mtodos. En ocasiones no usamos las tres divisiones, sino slo dos (eliminando
los atributos) o una (slo con el nombre de la clase). Los atributos y mtodos privados se pueden indicar
precedidos de un signo -, los pblicos del signo + y los protegidos de #. Un atributo derivado48 se puede
representar precedido de una barra (/). Un atributo de clase y un mtodo de clase se pueden representar
subrayados. Las restricciones de un atributo respecto de otro u otros se pueden representar colocando la
ecuacin entre llaves, como: {superficie = lado*lado}.
A continuacin hay una clase de ejemplo:
cVentana
-Posicion : Object
-ColorFondo : int
-Ancho : int
-Alto : int
-/ Superficie : long
-Titulo : String
#Owner : cVentana
+Mover()
+Dibujar()
+Borrar()
En general no se indican las operaciones que dan valores a los atributos o que los obtienen, pues se
las supone siempre existentes. Tampoco se suelen representar constructores o destructores.
Como se ve en el ejemplo, los atributos y los mtodos se describen con la notacin de algn lenguaje
de programacin. Aqu se ha elegido la sintaxis de Java. De todas maneras, es conveniente no utilizar aspectos
de la sintaxis que tengan un significado slo en un determinado lenguaje.
A menudo se suelen indicar las responsabilidades de la clase que se diagrama. Se entiende por
responsabilidad al contrato u obligacin de la clase, en el sentido que le da la metodologa de las tarjetas
CRC49 . Es una buena prctica indicar las responsabilidades en un diagrama de clases de alto nivel. Para hacerlo,
se introduce una nueva divisin en el rectngulo que representa la clase, con el ttulo Responsabilidades, y cada
una de las mismas precedida de un signo - -.
Las asociaciones entre clases se representan con lneas rectas, sobre las cuales se puede escribir un
texto descriptivo o rol de la relacin, as como el grado de multiplicidad, como se muestra en el diagrama que
sigue:
47
- 66 -
cPedido
cCliente
*
+Artculos de lnea
CLineaDePedido
Significado
Uno y slo uno
Cero o uno
De M a N (enteros naturales)
De 0 a muchos
De 0 a muchos (igual a *)
De 1 a muchos
De M a muchos
Las relaciones de herencia se representan como un rbol, con lneas rectas, con una flecha que apunta
hacia la clase ancestro. Cuando slo se representan jerarquas y las clases ancestros se encuentran siempre ms
arriba que las descendientes, la flecha puede omitirse (as hicimos en el captulo en que introdujimos el
concepto de herencia).
A continuacin se muestra una jerarqua de clases:
cCuenta
+Depositar()
+Retirar()
cCajaAhorro
cCuentaCorriente
+Depositar()
+Retirar()
cCAComun
+Depositar()
+Retirar()
+Depositar()
+Retirar()
cCAEspecial
+Depositar()
+Retirar()
- 67 -
CARLOS FONTELA
Una clase abstracta se representa en cursiva, y lo mismo ocurre con los mtodos abstractos, como se
muestra en la jerarqua anterior con las clases cCuenta y cCajaAhorro y los mtodos Depositar y Retirar en
estas clases.
UML supone que todos los mtodos son virtuales, por lo que se debera especificar los mtodos no
virtuales con la propiedad etiquetada {leaf}. Sin embargo, no haremos esta distincin en nuestros diagramas.
Las interfaces se representan con el estereotipo <<interface>>. La implementacin de una interfaz
por una clase puede representarse como la herencia, aunque con lnea punteada50 . A continuacin se muestra la
jerarqua anterior, a la cual se le agrega la interfaz iPersistente :
interface
iPersistente
+Guardar()
+Recuperar()
cCuenta
+Depositar()
+Retirar()
cCajaAhorro
cCuentaCorriente
+Depositar()
+Retirar()
+Depositar()
+Retirar()
cCAComun
cCAEspecial
+Depositar()
+Retirar()
+Depositar()
+Retirar()
Para expresar la composicin se hace con un rombo relleno del lado de la entidad mayor, como
muestra la figura que sigue.
cPoligono
cPunto
1
La agregacin tambin puede representarse, colocando un rombo vaco en vez de relleno. Sin
embargo, en general no se la representa pues, como ya dijimos, equivale a una simple asociacin.
A veces hay atributos y comportamiento propios de las asociaciones, y en esos casos conviene crear
clases para las mismas, llamadas clases de asociacin:
50
Esta forma es denominada forma cannica de representacin de interfaces. Si no se requiere demasiada informacin de la interfaz, se
la puede representar en la forma abreviada, que es un crculo pequeo y vaco.
- 68 -
cPersona
0..1
cEmpresa
cEmpleo
aPeriodo : Date
Estas tcnicas se utilizan sobre todo en asociaciones mltiples. En las asociaciones 1 a 1 no son
necesarias porque se pueden desplazar los atributos y mtodos de la asociacin a una de las clases.
Abajo hay otra forma de representar este concepto:
*
0..1
cPersona
cEmpleo
-/ Empleador
cEmpresa
-aPeriodo : Date
1
0..1
Se pueden utilizar diagramas de clases para representar tipo de datos que no sean clases. Por ejemplo,
el siguiente diagrama representa un tipo enumerado con el estereotipo <<enumeration>>:
enumeration
tDiasLaborables
-lunes
-martes
-miercoles
-jueves
-viernes
- 69 -
CARLOS FONTELA
Usuario XX
Cajero YY
DatosUsuarios
DatosCuentas
IngresaTarjeta()
VerificarTarjeta()
PedirClave()
ChequearUsuario (Tarjeta,Clave)()
PedirSaldo()
PedirSaldo(Usuario)()
Grabar()
PedirSaldo()
Terminar()
TerminarYGrabar()
<<destroy>> TerminarYGrabar()
X
Es factible representar un mensaje enviado al mismo objeto. Tal el caso de los mensajes
51
Un escenario es un flujo de eventos simple de un caso de uso. Puede pensarse tambin como una instancia de un caso de uso.
- 70 -
VerificarTarjeta y TerminarYGrabar.
La creacin de un objeto es provocada por un mensaje enviado desde otro objeto. En el ejemplo,
Transac ZZ se crea cuando el objeto Cajero YY le enva el mensaje InicioTransaccion. Las creaciones se suelen
acompaar con el estereotipo <<create>>.
La destruccin de un objeto se puede representar por una X en la lnea de vida, y se suelen
acompaar con el estereotipo <<destroy>>. Un objeto se puede destruir a s mismo (se suicida), o puede ser
destruido por otro. En el ejemplo, el objeto Transac ZZ se suicida envindose un mensaje TerminarYGrabar.
Un mensaje asncrono (el que no espera la respuesta del objeto con el que se comunic) se representa
con una flecha de media punta, como hicimos con Grabar, Terminar y TerminarYGrabar.
Se pueden representar los retornos de los mensajes, aunque slo conviene hacerlo cuando estn
diferidos en el tiempo. Se representan igual que los mensajes, pero en lneas de puntos. As se hizo con el
retorno de PedirSaldo.
Se puede representar el hecho de que en un momento dado un objeto tiene el foco de control, con un
rectngulo a lo largo de su lnea de vida.
Como conclusin, podemos decir de los diagramas de secuencia:
?? Su gran virtud es la simplicidad y el impacto visual.
?? Ayuda a ver el flujo del control y el ordenamiento temporal de los hechos, tan difcil de seguir
en POO, y sobre todo con eventos y RAD.
?? Si se desea enfatizar ms la organizacin de los objetos que el ordenamiento temporal, se
puede usar un diagrama de colaboracin, semnticamente equivalente al de secuencia.
?? Conviene colocar ms a la izquierda los objetos ms importantes en la interaccin que se est
diagramando.
?? Ayuda a encontrar los mtodos necesarios en las clases.
?? Si un objeto enva un mensaje a otro en un diagrama de secuencia, quiere decir que sus clases
respectivas deben estar relacionadas en el diagrama de clases. Dicho de otra manera, una
asociacin en el diagrama de clases se puede materializar en un enlace en el diagrama de
secuencia, que da lugar al envo de un mensaje.
?? Como un diagrama de secuencia slo debera representar un flujo de eventos de un caso de
uso, y como estos flujos de eventos deben ser lo ms simples posible, es deseable que no haya
bifurcaciones condicionales en los diagramas de secuencia, haciendo un diagrama de
secuencia para cada caso particular. Un diagrama cmodo para expresar bifurcaciones es el
diagrama de actividades.
?? Como todo diagrama, deben ser simples: ms que mostrar toda la comple jidad de un sistema,
deben ser herramientas de fcil visualizacin.
Diagramas de estados
El diagrama de estados es un modelo dinmico que muestra los cambios de estado que sufre un
objeto a travs del tiempo. Este tiempo puede ser toda la vida del objeto, pero ms usual es representar un
objeto dentro de un caso de uso. El objeto puede ser simple o tan complejo como se quiera (hasta se puede
representar una aplicacin completa).
El diagrama de estados, por lo tanto, es una abstraccin que representa los estados, eventos y
transiciones de estados para un objeto o un sistema. Es lo que se denomina una mquina de estados, que
muestra la secuencia de estados en la vida de un objeto, los eventos que modifican estados y las acciones y
respuestas del objeto.
La representacin grfica consiste en un grafo con nodos para los estados y arcos para las
transiciones, adems de un texto en correspondencia con los arcos que identifica los eventos.
A continuacin se muestra un diagrama simple del juego del ajedrez:
- 71 -
CARLOS FONTELA
Turno de
las blancas
Juegan
las blancas
Turno de
las negras
Jaque
Ganan las negras
Juegan
las negras
Jaque
mate
Tablas
Desde ya que este diagrama, no sirve para entender todos los estados por los que pasa el juego de
ajedrez, ni tampoco pretende cumplir este objetivo. Sin embargo, brinda una buena imagen de conjunto, por
ejemplo, para alguien que no conoce nada del juego. Siempre se puede refinar el diagrama para mostrar ms
estados y objetos.
Como se ve, los nicos componentes que deben aparecer en un diagrama de estados son los nodos y
los arcos, ms un nodo especial para indicar el comienzo y otro para indicar la finalizacin. En los nodos y los
arcos se pone una descripcin del estado o evento que corresponda.
Dentro del nodo que corresponde a cada estado se puede poner un texto adicional que especifique si
el objeto se encuentra realizando alguna accin. Asimismo, en la descripcin de las transiciones se puede poner
una condicin que se deba satisfacer, entre corchetes, o incluso parmetros que acompaen al evento.
No siempre tiene que haber un estado final. Esto ocurre, por ejemplo, en los sistemas empotrados,
que una vez instalados corren en forma continua.
Como dijimos, se trata de un diagrama sencillo. Por eso mismo, hay que tratar de que permanezca
sencillo cuidando el nivel de detalle, sin hacer diagramas muy complejos.
Para realizar un diagrama de estados:
?? Lo primero que hay que decidir es a qu eventos puede responder el objeto.
?? Hay que definir precondiciones y poscondiciones de estados inicial y final.
?? Hay que definir los estados estables.
?? Una vez realizados, se los puede probar haciendo un seguimiento de los eventos,
transiciones y estados.
Los diagramas de estados son tiles para diseadores, programadores y testers, pero debe balancearse
la informacin para que sean tiles, ya sea agrupando estados o ignorando ciertos eventos.
Son ideales en el caso en que una especificacin mediante estados sea ms ilustrativa que una que
describa actividades.
Tambin son una herramienta interesante en el caso de objetos reactivos, es decir, aquellos objetos
para los cuales la mejor forma de caracterizar su comportamiento sea sealar cul es su comportamiento frente
a estmulos provenientes desde fuera de su contexto, o aquellos que estn ociosos hasta el momento en que
reciben un evento.
Los diagramas de estado permiten tambin especificar la concurrencia de estados (veremos el
concepto de concurrencia en un captulo posterior).
Diagramas de actividades
Hemos dicho que el diagrama de actividades resalta el flujo de control entre objetos. Es, por lo tanto,
- 72 -
- 73 -
CARLOS FONTELA
Cliente
Cajero
Inserta
tarjeta
Pide clave
Ingresa
clave
Chequea
clave
Banco
[clave incorrecta]
[clave correcta]
Pide monto
Chequea
saldo
Procesa
transaccin
Retira dinero
Entrega
dinero
[saldo >=
monto]
Debita
cuenta
[saldo <
monto]
Muestra
saldo
Retira tarjeta
Expulsa
tarjeta
- 74 -
La notacin del diagrama es bsicamente la misma que la del de estados, aunque actan varios
objetos a los que se separa en calles (del ingls, swimlanes, calles de pruebas de natacin) verticales. La
separacin de tareas en paralelo se hace mediante una barra gruesa, y lo mismo ocurre con su unin posterior.
Un rombo permite denotar ramificaciones basadas en alguna condicin.
Diagramas y programas
Como el nuestro es un curso de programacin, cabe preguntarse para qu nos sirven los diagramas
vistos respecto de la elaboracin del programa. Como resumen:
?? Los diagramas de clases son tiles para construir la jerarqua de clases y analizar asociaciones.
?? A partir de los diagramas de secuencia es fcil hacer surgir el cdigo de los mtodos ms
complejos o con mucha interaccin entre objetos.
?? Lo mismo ocurre con los diagramas de estado para objetos reactivos.
?? Y con los diagramas de actividades en el caso en que los flujos son importantes.
?? Si las interacciones son importantes, el dia grama de secuencia conviene que acompae a la
documentacin y que se mantenga actualizado.
?? Los diagramas de estados son tiles para analizar objetos que sufren muchos cambios de
estados, para modelar sistemas reactivos o para describir globalmente un sistema.
Existen herramientas que generan cdigo a partir de diagramas UML. No obstante, hay que ser
precavidos: no siempre el cdigo generado es de buena calidad, fcil de mantener y extender. Por otro lado, hay
muchas herramientas que slo generan pantallas estndar, y otras que proclaman generar cdigo y slo nos
escriben los encabezados de los mtodos que declaramos en los diagramas.
Tambin hay herramientas que generan los diagramas UML a partir de cdigo. Este proceso,
denominado ingeniera inversa, siempre es incompleta, pues el paso de un diagrama que modela la realidad a
cdigo siempre importa una prdida de riqueza de informacin. La nica forma de paliar esta prdida es
incorporar comentarios al cdigo que luego puedan ser extrados por el utilitario que hace la ingeniera inversa.
DOCUMENTACIN INTERN A
Documentacin interna de la programacin
Documentar en el propio cdigo facilita mantener la documentacin actualizada. A esto se lo llama
documentacin interna.
En otros tiempos se ponan prlogos de mdulos, se haca un buen uso de estructuras, nombres claros
y del dominio del problema y sangras que denotaban las estructuras de control. Tambin se aconsejaba dejar
los comentarios muy abundantes fuera de los prlogos para secciones muy crticas, poco claras, avisos para el
mantenimiento y efectos colaterales locales. Adems, se recomendaba no usar los comentarios como remedio
para el cdigo poco claro, sino que ante todo estaba la necesidad de que el cdigo fuera bien legible. Todas
estas recomendaciones son vlidas an hoy.
Lo que tambin hay hoy son herramientas que facilitan la documentacin interna. En Java esto es
particularmente interesante por la condicin de estndar de la herramienta. En otros casos debemos confiar en
lo que nos ofrezcan las implementaciones particulares.
El caso de javadoc
El caso de Java en cuanto a documentacin interna, como decamos, es paradigmtico, y por eso lo
analizamos aparte.
- 75 -
CARLOS FONTELA
En Java, como en cualquier otro lenguaje de programacin, se pueden escribir comentarios, de forma
tal que stos no sean tomados en cuenta por el compilador. Se pueden hacer entre un signo /* y uno */ o bien
despus de una doble barra // hasta el final de la lnea.
Pero Java utiliza una sintaxis especial para marcar elementos de documentacin interna dentro de los
comentarios, a la vez que provee una herramienta que permite extraer esa documentacin de forma que sea til
al usuario de la misma.
Esto se hace mediante el programa javadoc , que toma algunos de los comentarios que se colocan en
el cdigo con marcas especiales y construye un archivo HTML con clases, mtodos y la documentacin que
corresponde, de modo de poder verla con cualquier navegador web. Este archivo HTML tiene el mismo
formato que toda la documentacin estndar de Java.
La documentacin a ser utilizada por el programa javadoc se inscribe en comentarios que empiezan
con /** y terminan con el habitual */. Dentro de estos comentarios especiales se pueden escribir comandos para
que javadoc los interprete, siempre precedidos de @.
Se pueden documentar clases, atributos y mtodos, y siempre se debe poner la documentacin
inmediatamente antes de la declaracin respectiva. Por ejemplo:
/** documentacin de la clase */
public class cRectangulo {
/** documentacin del atributo ladoMayor */
double ladoMayor;
/** documentacin del mtodo Dibujar */
void Dibujar () { }
}
javadoc procesar la documentacin que se escriba para atributos y mtodos pblicos y protegidos,
sin emitir nada para los privados y friendly 52 , lo cual es bastante lgico porque se presume que la
documentacin es para el programador cliente y ste slo necesita lo pblico y protegido.
En cualquier lnea de documentacin para javadoc se podr agregar formatos HTML de cualquier
tipo para formatear la salida del archivo de documentacin. Slo deberan omitirse los formatos de ttulos <h1>
y <hr>, pues javadoc los utiliza para poner sus propios ttulos. Por ejemplo, el siguiente fragmento es vlido y
generar la inclusin de HTML en el documento final:
/** <h2> La clase que sigue es muy importante: </h2> */
Cuando se quiera indicar una referencia a otra clase, desde una documentacin de clase, mtodo o
atributo, se podr utilizar el comando @see:
@see nombre-de-clase
@see nombre-de-clase-con-todos-sus-calificadores
@see nombre-de-clase-con-todos-sus-calificadores#nombre-mtodo
De todas maneras, se puede incluir los atributos y mtodos privados agregando private en la lnea de comandos.
- 76 -
Con el comando @author se puede poner tambin cualquier informacin sobre el autor o autores de
una clase:
@author informacin-del-autor
Con el comando @since se puede informar desde cu ndo o qu versin se encuentra vigente una
determinada capacidad de la clase.
Para documentar parmetros, valores devueltos y excepciones 53 de un mtodo se usan los comandos
@param, @return y @throws:
@param nombre-de-parmetro descripcin
@return descripcin
@throws clase-de-excepcin-con-sus-calificadores descripcin
- 77 -
CARLOS FONTELA
- 78 -
Solucin
Diagrama de actividades:
- 79 -
CARLOS FONTELA
Aspirante
Sistema inscripcin
Profesor
Coordinador
Evuala situacin
Evuala situacin
Chequea clave
[clave incorrecta]
[clave correcta]
[correlativas insuficientes]
[correlativas suficientes]
[solicitud no aprobada]
[solicitud no aprobada]
[solicitud aprobada]
Recibe rechazo
[solicitud aprobada]
Notifica alumno
Inscribe alumno
- 80 -
Diagrama de clases:
1
cSistemaInscripcion
cPersona
*
cCurso
+ChequearCorrelativas()
+InscribirAlumno()
*
+PantallaInscripcion()
+ElegirCurso()
cDocente
cAspirante
+ConsultarOpinion()
+NotificarAlumno()
cProfesor
+ConsultarOpinion()
cCoordinador
+ConsultarOpinion()
cCurriculaAlumno
+ConsultarCurricula()
El contorno ms grueso de clase cSistemaInscripcion indica que se trata de un objeto activo, que
puede disparar llamadas concurrentes.
Debe notarse que, cuando el sistema de inscripcin se comunica con una persona, sea sta un
aspirante o un docente, lo que hace es trabajar con un objeto cliente, que en realidad es una interfaz de usuario
que ve la persona. Reconozcamos que esta relacin fue la nica razn que justific la existencia de la clase
cPersona en el diagrama.
Diagrama de secuencia:
- 81 -
CARLOS FONTELA
unAspirante
SistemaInscripcion
unCurso
unProfesor
Coordinador
unaCurriculaAlumno
PantallaInscripcion()
ElegirCurso()
ChequearCorrelativas()
ConsultarOpinion()
ConsultarCurricula()
ConsultarOpinion()
ConsultarOpinion()
ConsultarCurricula()
ConsultarOpinion()
NotificarAlumno()
InscribirAlumno()
El contorno ms grueso del objeto SistemaInscripcion indica que se trata de un objeto activo, que
puede disparar llamadas concurrentes. Precisamente, las llamadas a los dos mtodos ConsultarOpinion se
hacen asncronas y en paralelo. Estos conceptos se estudiarn en el captulo de concurrencia.
Ntese que no todos los estados del diagrama de actividades quedaron reflejados en el diagrama de
secuencia, aunque s los principales. Tambin fue necesario introducir dos objetos nuevos, el curso y la
currcula del alumno.
- 82 -
Solucin
Mostrando hora
[Botn A apretado]
- 83 -