Sei sulla pagina 1di 83

Programacin orientada a objetos

y tcnicas avanzadas de programacin

Carlos Fontela 2003

CARLOS FONTELA

-2 -

PROGRAMACIN ORIENTADA A OBJETOS

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 2

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.

-3-

CARLOS FONTELA

Cuando en el libro decimos Java nos referimos a la versin 2 del lenguaje, C++ es el d la definicin e 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 c uenta 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

El personaje de Lewis Carroll.

-4 -

PROGRAMACIN ORIENTADA A OBJETOS

NDICE DE CONTENIDOS
1. Prlogo 3

PARTE I: PROGRAMACIN ORIENTADA A OBJETOS


2. Introduccin: la orientacin a objetos Conceptos previos Programacin estructurada Programacin modular Abstraccin Ocultamiento de implementacin Hacia un nuevo paradigma Calidad del software Orientacin a objetos Reduccin de la brecha entre el mundo de los problemas y el mundo de los modelos Aumento del nivel de complejidad de los sistemas Reutilizacin y extensin del cdigo Uso de prototipos Programacin en ambientes de interfaz de usuario grfica Programacin por eventos Programacin orientada a objetos Significado Lenguajes de programacin orientada a objetos Herramientas visuales y de desarrollo rpido de aplicaciones Programacin orientada a objetos: generalidades y encapsulamiento Objetos y clases Objetos Clases Encapsulamiento La abstraccin en programacin orientada a objetos El ocultamiento en programacin orientada a objetos Encapsulamiento Tipos especiales de atributos Atributos de clase Atributos con valor constante Atributos con restricciones Mtodos de clase Terminologa de objetos Estados, eventos y transiciones Operaciones Estado observable de un objeto Pasaje por valor y por referencia en programacin orientada a objetos Constructores Destructores Sobrecarga Implementacin de objetos y clases en distintos lenguajes Objetos y clases en Object Pascal Objetos y clases en C++ Objetos y clases en Java Ejercicio resuelto: implementacin de nmeros racionales Enunciado

8
9 9 9 10 10 11 13 14 14 15 16 16 16 17 17 17 17 18 19 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

3.

-5-

CARLOS FONTELA

Solucin en Object Pascal Solucin en C++ Solucin en Java Ejercicio resuelto: implementacin de conjuntos Enunciado Solucin en Object Pascal Solucin en C++ Solucin en Java 4. Programacin orientada a objetos: reutilizacin y extensin del cdigo Reutilizacin con composicin Composicin Agregacin Reutilizacin con herencia Definicin y ejemplos Utilizacin de herencia Herencia mltiple Herencia con excepciones Especializacin con menos atributos Constructores, destructores, herencia y composicin Atributos y mtodos protegidos Generalizacin, especializacin e instanciacin Interfaces Concepto de interfaz Un ejemplo de interfaces: la clase adaptadora Eleccin de implementacin: composicin, herencia o interfaces? Implementacin de reutilizacin del cdigo en distintos lenguajes Composicin, herencia e interfaces en Object Pascal Composicin y herencia simple y mltiple en C++ Composicin, herencia e interfaces en Java Ejercicio resuelto: implementacin de matrices Enunciado Solucin en Object Pascal Solucin en C++ Solucin en Java Ejercicio resuelto: uso de la implementacin de matrices Enunciado Solucin en Object Pascal Solucin en C++ Solucin en Java Programacin orientada a objetos: polimorfismo Polimorfismo Polimorfismo y vinculacin tarda Redefinicin Sobrecarga y redefinicin Objetos polimorfos o conversin de tipo automtica Mtodos virtuales Constructores y destructores virtuales Grados de polimorfismo Clases y mtodos abstractos Clases abstractas Mtodos abstractos Clases utilitarias y clases sin estado Uso de informacin de tipos en tiempo de ejecucin

34 34 34 35 35 35 35 35 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 51 51 51 51 52 52 53 54 55 55 55 56 56 57

5.

-6 -

PROGRAMACIN ORIENTADA A OBJETOS

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 6. Documenta ndo la programacin Documentacin en general UML para documentar la programacin UML Diagramas de clases Diagramas de secuencia Diagramas de estados Diagra mas de actividades Diagramas y programas Documentacin interna Documentacin interna de la programacin El caso de javadoc Estndares de nomenclatura Ejercicio resuelto: diagramas de actividades, clases y secuencia Enunciado Solucin Ejercicio resuelto: un diagrama de estados Enunciado Solucin

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 -

PROGRAMACIN ORIENTADA A OBJETOS

2. INTRODUCCIN: LA ORIENTACIN A OBJETOS


CONCEPTOS PREVIOS
Hay una serie de tcnicas, anteriores en el tiempo a la orientacin a objetos, que han tenido una fuerte influencia sobre la misma. Entre ellas, la programacin estructurada, la programacin modular, la abstraccin y el ocultamiento de informacin. Dado que, si bien son tcnicas desarrolladas fuera del paradigma de objetos, permanecen en l, las vamos a repasar a continuacin. Programacin estructurada La programacin estructurada es un concepto que surge como respuesta a la crisis del software de los aos 60, cuando el desarrollo de programas estaba ocupando una porcin cada vez mayor de los costos de computacin y, a su vez, el mantenimiento de los mismos era cada vez ms inmanejable. La programacin estructurada se convirti, hace unos 15 aos, en un must, y todo producto que se lanzaba al mercado se proclamaba estructurado. El primer inconveniente que se atac fue el uso del ya olvidado Goto 6 , sobre todo luego de un artculo de Dijkstra titulado Goto considered harmful (El Goto considerado daino). A menudo se ha resumido la programacin estructurada como el rechazo del Goto y nada ms. Esta bsqueda de la supresin del uso del Goto haba provocado la enunciacin del teorema de Bohm-Jacopini [13], ya en 1966, bajo la caracterizacin de una entrada, una salida: "Cualquier segmento de programa con una entrada y una salida que tenga todas las proposiciones en algn camino de la entrada a la salida se puede especificar usando slo secuencia, seleccin e iteracin". Sin embargo, dentro de los propsitos de la programacin estructurada haba otros, de los cuales los ms importantes parecen ser: ?? Utilizar un estilo disciplinado de programacin. ?? Ser una tcnica que lleve a programas altamente modificables. ?? Sencillez, claridad y elegancia. Estos objetivos se logran slo parcialmente con la eliminacin del Goto y su reemplazo por las tres estructuras fundamentales de secuencia, seleccin e iteracin. Otros aspectos, menos formalizados, apuntaban a usar lenguajes que poseyeran unas pocas sentencias de uso habitual, con construcciones conceptualmente simples y ampliamente aplicables en la prctica, sin reglas demasiado permisivas, el uso de nombres claros, no utilizar un identificador para propsitos mltiples, abundancia de comentarios aclaratorios, uso de sangra para aumentar la legibilidad y denotar la estructura del programa, el aislamiento de las dependencias de la mquina en unas cuantas rutinas separadas, etc.. Fue la primera vez que el rendimiento y las caractersticas tcnicas dejaron de ser las nicas consideraciones de calidad de los sistemas de software. Los lenguajes de programacin como Fortran y Algol se hicieron ms estructurados, y luego surgieron nuevos lenguajes, como C y Pascal. El concepto de programacin estructurada lo he analizado a fondo en mi obra Curso de Programacin [5], en el tomo I.
6

Ms correctamente, ruptura de secuencia o interrupcin incondicional de la secuencia.

-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

Esto ha dado lugar a que en general se las conozca como libreras.

- 10 -

PROGRAMACIN ORIENTADA A OBJETOS

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 introduci efectos colaterales. Por lo tanto, podr agregar nuevas operaciones, pero debo r garantizar que las antiguas operaciones permanecen y mantienen la misma interfaz.
8

Esto lo tom de Boria [11].

- 12 -

PROGRAMACIN ORIENTADA A OBJETOS

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

La expresin la tom de Muller [10]. Hoy en da existen mtricas para evaluar la complejidad, pero en este contexto preferimos este anlisis simplificado.

- 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 -

PROGRAMACIN ORIENTADA A OBJETOS

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 q ue favorecen el desarrollo veloz de clases similares a las ya existentes, trabajando siempre sobre comportamiento

- 16 -

PROGRAMACIN ORIENTADA A OBJETOS

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.

PROGRAMACIN ORIENTADA A OBJETOS


Significado En la programacin imperativa tradicional, los algoritmos se describen en base a procesos (ya sea en forma secuencial o mediante concurrencia). Por ejemplo, s pueden escribir en Pascal o C una serie de e subprogramas para el manejo de pantallas, y sern exactamente eso: procedimientos o funciones que describen cmo se abre una ventana, cmo se la cierra, cmo se la limpia, etc.. As, los algoritmos se expresan mediante procesos y stos como una secuencia de tareas a realizar por la computadora. Por eso este tipo de programacin se llama procedimental o imperativa. La programacin orientada a objetos (POO) encara la resolucin de cada problema desde otra ptica: la ptica del objeto. As, para resolver el caso anterior se define un objeto pantalla y se definen todas las acciones (denominadas mtodos) que ese objeto puede realizar (comportamiento), cada vez que se le enva un cierto mensaje o solicitud. Las nicas acciones que se le pueden hacer a un objeto vienen dadas por los mtodos. Lo que se define es el comportamiento de los objetos frente a mensajes o solicitudes provenientes de otros objetos. Incluso los programas se disparan con el envo de un mensaje a un objeto. Desde el punto de vista de los objetos, un programa es un conjunto de objetos colaborando, o bien, un conjunto de objetos enviando mensajes y respondiendo a otros mensajes.

- 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 h emos 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 -

PROGRAMACIN ORIENTADA A OBJETOS

?? 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

La mayora de estos ambientes maneja POO. Por ejemplo, usuarios Delphi, usuarios Visual Basic.

- 20 -

PROGRAMACIN ORIENTADA A OBJETOS

3. PROGRAMACIN ORIENTADA A OBJETOS: GENERALIDADES Y ENCAPSULAMIENTO


OBJETOS Y CLASES
Objetos Un objeto es toda entidad activa de un programa. Tambin puede decirse que es cualquier cosa, real o abstracta, de la cual almacenamos datos y la forma de manipular esos datos. Una analoga con la programacin imperativa nos permitira comparar los objetos a las variables, aunque no sera del todo exacto. Un objeto queda definido por las siguientes propiedades: ?? Estado ?? Comportamiento ?? Identidad El estado de un objeto define la situacin del mismo en un momento establecido en el tiempo. Los valores que se almacenan de un objeto se llaman generalmente atributos y pueden ser, a su vez, objetos o variables del lenguaje de programacin que se utilice. Hay dos tipos de atributos: os constantes, que no cambian con el tiempo, y los dinmicos 15 . Por l ejemplo, del estado del objeto libro de programacin, podemos decir, que entre sus atributos constantes estn el ser de hojas blancas, estar escrito con letras negras, y dems, mientras q como atributo dinmico ue podramos decir que est abierto en una pgina en la que se lee el ttulo objetos y clases. El estado, por lo tanto, viene dado por los valores de todos los atributos del objeto en un momento dado. Ntese que estos atributos estarn estructurados de una forma tal que establecern lo que llamamos la estructura interna en los TDA. El comportamiento es la manera en que un objeto reacciona ante mensajes recibidos, bsicamente enviando mensajes a otros objetos, respondiendo al que le enva el mensaje o cambiando de estado. Las distintas formas de respuesta se denominan mtodos , y corresponden a procedimientos y funciones, segn el lenguaje y su notacin. A priori, para que un objeto le enve un mensaje a otro debera conocerlo, pero h ay soluciones que implementan canales, que permiten que un objeto se comunique con otro conociendo el canal, y no necesariamente al otro objeto. De nuevo, tomando el ejemplo de este libro, podemos decir que puede leerse secuencialmente, puede ser abierto en una pgina en forma directa, puede ser destruido, etc.. En POO, un objeto puede satisfacer solamente ciertos requerimientos, definidos por su interfaz, que es el conjunto de los mtodos aplicables a ese objeto. Son la propia interfaz y la implementacin de la misma quienes definen el comportamiento. La identidad es lo que diferencia a un objeto de todos los otros del mismo tipo. En la programacin procedimental la analoga lgica es el nombre de variable 16 . Por ejemplo, este libro que tiene entre sus manos tiene una identidad que lo diferencia de los dems
15 16

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

libros de la misma edicin, an cuando tengan el mismo comportamiento y estado. Clases Sin embargo, no puede haber objetos que no respondan a un concepto. Una clase responde a la idea de concepto, entendido como una nocin compartida que se aplica a determinados objetos en forma consciente. Para ver si un objeto est comprendido en un concepto se le aplican ciertas pruebas de reconocimiento. Por ejemplo, para saber si un determinado objeto es un pino, podramos ver si es un rbol, si tiene frutos en forma de pia, si tiene las hojas cilndricas y alargadas, etc.: si no pasa alguna de las pruebas concluiremos que no es un pino. Una clase es un conjunto de objetos con la misma estructura y el mismo comportamiento. Es una nocin similar a la de tipo en programacin estructurada (de hecho, hay quien habla de tipo de objeto y reserva el nombre clase para otros usos). Responde a la idea de concepto. En nuestro ejemplo, se podra hablar de la clase de todos los libros de esta edicin. La relacin entre objeto y clase se denomina instanciacin . Cada objeto particular es una instancia de la clase. Una clase es como un molde desde el que se crean los objetos. De all que casi toda nuestra atencin como programadores se centre en las clases y no tanto en los objetos. Dado que todos los objetos de una clase poseen la misma estructura y el mismo comportamiento, la declaracin de una clase siempre tiene la descripcin de la estructura interna (atributos) y del comportamiento (mtodos) comunes de la misma. Ntese la diferencia fundamental entre un objeto y una clase. Un objeto es algo concreto, aunque pueda ser tanto real como abstracto. Una clase es una abstraccin de un concepto que engloba a todos los objetos. Por ejemplo, pas, idioma, elefante , libro, taza, auto, son clases, mientras que Bolivia, castellano, Dumbo, El Quijote de la Mancha, la taza amarilla en que tomo mi desayuno, el auto de patente RRR-222, son objetos.

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 -

PROGRAMACIN ORIENTADA A OBJETOS

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.

TIPOS ESPECIALES DE A TRIBUTOS


En este tem vamos a analizar algunos tipos de atributos que tienen un inters particular: ?? De clase. ?? Con valor constante. ?? Con restricciones. Atributos de clase Se denominan atributos de clase aquellos que tienen el mismo valor para cada objeto de la clase. A los atributos que no son de clase se los suele llamar atributos de instancia.
17

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 -

PROGRAMACIN ORIENTADA A OBJETOS

cambio en la manera de pensar. Cuando se trabaja con objetos, los datos elementales de un objeto o clase se denominan atributos. El cdigo para trabajar con un objeto es el comportamiento (en programacin tradicional hablaramos de funcionalidad) de ese objeto. El comportamiento del objeto se define en base a sus mtodos 18 . Un tipo de objetos se denomina clase , y a las variables de esa clase se las denomina objetos o instancias de la clase. A las invocaciones a los mtodos se las llama mensajes o solicitudes. Un mensaje es una comunicacin que un objeto enva a otro con la expectativa de desencadenar una actividad. El ocultamiento de la informacin cuando se implementan tipos abstractos de datos, adems de poner juntos los atributos y sus mtodos, se denomina encapsulamiento.

ESTADOS, EVENTOS Y TRANSICIONES


El estado de un objeto est representado por el conjunto de valores adoptado por los atributos y vnculos de un objeto en un momento dado. Podemos generalizar este concepto diciendo que es una situacin de un objeto durante la cual satisface una condicin, realiza una actividad o espera un evento. Los cambios de estado que sufren los objetos se deben a estmulos o mensajes recibidos de otros objetos, cuyos efectos generalmente llamamos eventos. Un evento indica la aparicin de un estmulo que puede disparar una transicin de estados. Es la especificacin de un acontecimiento significativo, como una seal recibida, un cambio de estado o el simple paso de un intervalo de tiempo. Los eventos pueden ser internos o externos al sistema. Los eventos pueden ser asncronos, cuando el emisor de la seal puede seguir trabajando sin esperar una respuesta, o sncronos, si debe quedarse esperando la respuesta del receptor. Por ejemplo, las seales son asncronas y las llamadas a mtodos son sncronas. En captulos posteriores analizaremos nuevamente el concepto de evento. Una transicin entre dos estados indica que un objeto que est en un primer estado realizar ciertas acciones y entrar en un segundo estado cuando ocurra algn evento especificado y se satisfagan ciertas condiciones.

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

Pasaje por valor y por referencia en programacin orientada a objetos En la programacin procedimental es usual, cuando se trabaja con subprogramas con parmetros, hablar de variables que se pasan por valor, de las que no se desea que su estado se modifique en el subprograma, y otras que se pasan por referencia, que van a ser modificadas en el mismo. Esto no debiera ser as en POO. La idea de los mtodos de la POO es que slo debieran cambiar el estado del objeto para el cual est siendo llamado el mtodo. Si se intenta modificar el estado de otro objeto se dice que estamos introduciendo efectos colaterales, lo cual a veces se hace, aunque deben tomarse los siguientes recaudos: ?? Analizar si tiene sentido ?? Analizar si esto queda claro para el usuario ?? Analizar si es probable que cause problemas En todo otro caso, los efectos colaterales deben evitarse. De todas maneras, en los lenguajes que trabajan con un modelo de memoria con referencias es imposible evitar el pasaje por referencia. Constructores Los constructores son mtodos especiales que efectan tareas de inicializacin oculta y necesaria. Por lo tanto, antes de usar un objeto se debe invocar a su constructor. Sin embargo, no todos los compiladores de lenguajes verifican esta condicin, quedando en estos casos a cargo del programador. Como consecuencia de lo anterior, cada clase debe tener un constructor. En Java y en C++ siempre hay un constructor con el nombre de la clase, aunque se lo puede redefinir con constructores definidos por el programador. En Object Pascal, en cambio, todo queda a cargo del programador. Dado que el constructor es lo primero que se invoca para cada objeto, es una p rctica comn y recomendable usarlos para inicializar valores de los atributos. En cuanto a la invocacin de los constructores, se suelen cometer dos errores comunes: invocarlo ms de una vez o no invocarlo nunca. Por eso, conviene invocarlo al principio del bloque de cdigo en el que fue declarado el objeto. Esta recomendacin la hacemos para Object Pascal, pues en Java y C++ el constructor se invoca automticamente cuando se crea la instancia. Destructores Los destructores son mtodos que cumplen tareas de liberacin de memoria y vnculos con otros objetos. Sin embargo, no es un concepto que exista en todos los lenguajes. En Java, por ejemplo, no hay destructores debido a que tiene un mecanismo de recoleccin automtica de basura. En los lenguajes en los que no se hace recoleccin de basura, es conveniente llamar al destructor de la clase al final de la vida de un objeto, chequeando no invocarlo ms de una vez. Una buena posibilidad es llamar al destructor al final del bloque de cdigo en el que fue declarado, aunque no siempre es posible o recomendable. Sobrecarga La mayora de los lenguajes de POO permiten que un subprograma tenga el mismo nombre que otros, con la condicin de que tengan distinta cantidad de parmetros o stos sean de tipos distintos. Es una propiedad que permite definir diferentes implementaciones para la misma operacin. Este concepto se denomina sobrecarga. En Java, en donde los subprogramas slo pueden definirse como mtodos dentro de una clase, se podr escribir:

- 26 -

PROGRAMACIN ORIENTADA A OBJETOS

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;

Sin embargo, sera incorrecto declarar:


function Potencia (M: Integer; N:Integer) : Real; overload; { incorrecto: difiere slo en el tipo del resultado }

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; }

La funcin anterior se puede invocar, por ejemplo:


cComplejo z, u, w; ... w = z + u;

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.

IMPLEMENTACIN DE OBJETOS Y CLASES EN DI STINTOS LENGUAJES


Objetos y clases en Object Pascal En Object Pascal, un objeto se declara en la declaracin de variables y una clase en la de tipos. Por ejemplo, la siguiente declaracin introduce un objeto Z como instancia de la clase cComplejo:
var Z : cComplejo;

Y esta otra introduce la clase cComplejo:


type cComplejo = class private Re, Im : Real; public constructor Create (R,I: Real); procedure Suma (X,Y: cComplejo); procedure Resta (X,Y: cComplejo); function Modulo : Real; function Argumento : Real; function ParteReal : Real; { ... ms operaciones ... } end;

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 ... }

El cliente de la implementacin de cComplejo escribir acciones como:


Z.Sumar(V,W);
19

{ Z, V y W deben ser instancias de cComplejo }

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 -

PROGRAMACIN ORIENTADA A OBJETOS

X := Z.Modulo; { Z es una instancia de cComplejo y X un Real }

Hay varias cosas que se pueden destacar aqu: ?? La declaracin de clases debe hacerse con la palabra class para asegurar el modelo de memoria que luego se describir. Versiones anteriores de Object Pascal usaban la palabra object, que se mantiene por compatibilidad hacia atrs, pero no se recomienda. ?? Los atributos y mtodos privados son visibles, no slo dentro de la clase en la cual estn declarados, sino en todo el archivo en el cual se los declar (programa o unidad de biblioteca). ?? La declaracin hecha con las palabras public o private afecta a todos los atributos y mtodos que le siguen, hasta que aparezca una directiva en contrario. ?? Para acceder a un atributo se hace con el nombre del objeto seguido de punto y el nombre del atributo, como con las variables compuestas (record) de Pascal. ?? Los mtodos hacen uso de un parmetro adicional a los que figuran en su encabezado. Este parmetro se denomina Self y el cuerpo de cada mtodo est enmarcado por un with Self do implcito. Es por eso que en cada mtodo de los de arriba es como si faltase un parmetro, y es por el with implcito que podemos colocar directamente los nombres de los atributos Re e Im sin poner Self. delante. ?? El parmetro Self es una referencia a un objeto instancia de la clase en cuestin. Por el momento, podramos decir que es un parmetro por referencia. ?? En las llamadas a mtodos el argumento que corresponde a Self se pone por delante del nombre del mtodo. En este sentido, Self es una referencia al objeto para el cual se est llamando al mtodo. ?? Los constructores y destructores pueden tener cualquier nombre que desee el programador, aunque por cuestiones que analizaremos ms adelante conviene que se llamen Create y Destroy. La llamada a un constructor no se hace de la forma habitual que tiene Pascal para llamar procedimientos. Por ejemplo, en el caso anterior se llamara as al constructor:
Z := cComplejo.Create(2,3);

Un mtodo de clase se puede indicar en Object Pascal anteponindole la palabra class en su declaracin. Los atributos de clase no estn soportados en Object Pascal, debiendo simularse con mtodos de clase y variables declaradas en la parte de implementacin. Object Pascal no implementa atributos con valor constante, por lo que el programador debe garantizar la constancia mediante la inicializacin en el constructor y sin proveer forma de alterarlos con ningn otro mtodo. Los atributos con restricciones n estn implementados, por lo que debe hacerlo el programador, o asegurando que tanto el constructor como todos los mtodos modificadores no puedan alterar la restriccin. En Object Pascal existe el concepto de propiedad, que permite mejorar el encapsulamiento. Por ejemplo, en el caso de los complejos se podra definir una propiedad PReal:
property PReal:Real read Re write AsignarReal;

donde el mtodo AsignarReal se debera definir:


procedure cComplejo.AsignarReal (PR:Real); begin Re := PR end;

De aqu en ms se podra utilizar directamente la propiedad PReal, como:

- 29 -

CARLOS FONTELA

Z.PReal := 4; { llama a Z.AsignarReal(4) } X := Z.PReal; { obtiene directamente el valor del atributo Z.Re }

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);

Y la declaracin que sigue sera la de la clase cComplejo:


class cComplejo { private: double re, im; public: cComplejo (double x, double y); // constructor ~cComplejo; // destructor void suma (cComplejo x, cComplejo y); void resta (cComplejo x, cComplejo y); double modulo(); double argumento(); double parteReal(); // ... ms operaciones ... } ;

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 -

PROGRAMACIN ORIENTADA A OBJETOS

// ... continan las implementaciones ...

El cliente de la implementacin de cComplejo escribir acciones como:


z.sumar(v,w); x = z.modulo(); { z, v y w deben ser instancias de cComplejo } { z es una instancia de cComplejo y x un double }

Hay varias cosas que se pueden destacar aqu: ?? Los atributos y mtodos privados son visibles slo dentro de la clase en la cual estn declarados. ?? La declaracin hecha con las palabras public o private afecta a todos los atributos y mtodos que le siguen, hasta que aparezca una directiva en contrario. ?? Para acceder a un atributo se hace con el nombre del objeto seguido de punto y el nombre del atributo, como con las variables compuestas (struct) de C. ?? Los mtodos hacen uso de un parmetro adicional a los que figuran en su encabezado, que es un puntero. Este puntero se denomina this y apunta a un objeto instancia de la clase en cuestin. En el cuerpo de cada mtodo, cuando se invoca un mtodo o se hace uso de un atributo sin especificar la instancia se refiere a *this. Es por eso que en cada mtodo de los de arriba es como si faltase un parmetro y podemos colocar directamente los nombres de los atributos re e im sin poner this-> delante. ?? En las llamadas a mtodos el argumento que corresponde a this se pone por delante del nombre del mtodo. En este sentido, this es un puntero al objeto para el cual se est llamando al mtodo. ?? Los constructores llevan el mismo nombre de la clase. Si no se declara ninguno, est definido un constructor por omisin, sin parmetros. ?? Los destructores llevan el mismo nombre de la clase precedidos por el carcter ~. Si no se declara ninguno, est definido un constructor por omisin, sin parmetros. El destructor es siempre invocado automticamente cuando el programa sale del mbito en el cual es declarado un objeto. Se pueden declarar algunos mtodos como funciones en lnea mediante la directiva inline. Una funcin de lnea es aqulla que el compilador expande en el punto donde es llamada en lugar de ser realmente llamada. Es decir, no se genera un cdigo separado para la funcin de lnea, sino su cdigo se copia en cada lugar en que la funcin es invocada21 . Su ventaja es que la invocacin se hace ms velozmente, debido a que se eliminan los tiempos de vinculacin entre mdulos y se ahorran prlogos y eplogos de la funcin compilada aparte que en el caso de funciones pequeas podran ser ms largos que el cuerpo de la propia funcin. Como desventaja, si la funcin de lnea es invocada varias veces en un programa, se la copia tantas veces como es llamada, por lo que aumenta el tamao del cdigo que sera necesario si fuera una funcin corriente. Como corolario, debe tratarse que las funciones de lnea sean las ms cortas de un programa y que se invoquen pocas veces. Se puede hacer que una funcin que no es un mtodo miembro de una clase tenga acceso a la parte privada de la misma, declarndola amiga22 de la clase. Por ejemplo:
class c { 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 { static int color; // atributo de clase static int getColor(); // mtodo de clase const int limiteIzquierdo = 0; // atributo con valor constante int limiteSuperior = 0; // atributo con valor por defecto }

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);

Y la declaracin que sigue sera la de la clase cComplejo:


public class cComplejo { private double re, im; public cComplejo (double x, double y) { // constructor re = x; im = y; } public void suma (cComplejo x, cComplejo y) { re = x.re + y.re; im = x.im + y.im;

- 32 -

PROGRAMACIN ORIENTADA A OBJETOS

} 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(); { z es una instancia de cComplejo y x un double }

Hay varias cosas que se pueden destacar aqu: ?? Los atributos y mtodos privados son visibles slo dentro de la clase en la cual estn definidos. ?? La declaracin hecha con las palabras public o private afecta slo al atributo o mtodo que le sigue. ?? Si a un atributo o mtodo no se le especifica la visibilidad, sta es la que Java adopta por omisin, conocida como friendly, que implica que ese atributo o mtodo es visible para sus descendientes y dentro del paquete 23 en que est incluida la clase, pero privado para cualquier clase fuera del paquete. ?? Las clases tambin tienen visibilidad (pblicas o friendly , nicamente). Una clase pblica va a ser accesible desde fuera para instanciarla, mientras una friendly slo va a poder accederse desde las clases del mismo paquete. Slo puede haber una clase pblica por archivo fuente y debe tener exactamente el mismo nombre d archivo 24 . No hay clases privadas, pero si no el quiero que una clase pueda instanciarse desde fuera de mi cdigo fuente, puedo hacer privado su constructor. ?? Para acceder a un atributo se hace con el nombre del objeto seguido de punto y el nombre del atributo, como con las variables compuestas (struct/record) de C y Pascal. ?? Los mtodos hacen uso de un parmetro adicional a los que figuran en su encabezado, que es una referencia a un objeto instancia de la clase en cuestin, llamada this. En el cuerpo de cada mtodo, cuando se invoca un mtodo o se hace uso de un atributo sin especificar la instancia se refiere a this. Es por eso que en cada mtodo de los de arriba es como si faltase un parmetro y podemos colocar directamente los nombres de los atributos re e im sin poner this. delante. ?? En las llamadas a mtodos el argumento que corresponde a this se pone por delante del nombre del mtodo. En este sentido, this es una referencia al objeto para el cual se est llamando al mtodo. ?? Los constructores llevan el mismo nombre de la clase. Si no se declara ninguno, est definido un constructor por omisin, sin parmetros. Pero si se declara algn constructor con parmetros, deja de existir el constructor sin parmetros.
23

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 { static int color; // atributo de clase static int getColor(); // mtodo de clase final int limiteIzquierdo = 0; // atributo con valor constante // funciona slo si el atributo no es un objeto int limiteSuperior = 0; // atributo con valor por defecto }

EJERCICIO RESUELTO: IMPLEMENTACIN DE NMEROS RACIONALES


Enunciado Escribir una clase que implemente el TDA cRacional, que maneje nmeros racionales de modo tal de poder hacer las siguientes operaciones: creacin en base al numerador y denominador, igualdad, suma, resta, producto, cociente, conversin a real y simplificacin. Solucin en Object Pascal Ver apndice A: Implementacin de nmeros racionales Solucin en C++ Ver apndice B: Implementacin de nmeros racionales Solucin en Java Ver apndice C: Implementacin de nmeros racionales

- 34 -

PROGRAMACIN ORIENTADA A OBJETOS

EJERCICIO RESUELTO: IMPLEMENTACIN DE CONJUNTOS


Enunciado Escribir una clase para implementar conjuntos de enteros de valores desde 0 hasta 5000 utilizando arreglos de elementos lgicos. Solucin en Object Pascal Ver apndice A: Implementacin de conjuntos Solucin en C++ Ver apndice B: Implementacin de conjuntos Solucin en Java Ver apndice C: Implementacin de conjuntos

- 35 -

CARLOS FONTELA

4. PROGRAMACIN ORIENTADA A OBJETOS: REUTILIZACIN Y EXTENSIN DEL CDIGO


Llamamos reutilizacin al uso de clases u objetos desarrollados y probados en un determinado contexto, para incorporar esa funcionalidad en una aplicacin diferente a la de origen. En este sentido, el uso de TDA bien definidos ya constituye reutilizacin del cdigo. La extensin se basa en aprovechar las clases desarrolladas para una aplicacin, utilizndolas en la construccin de nuevas clases para la misma u otra aplicacin. De todos modos, como la extensin es una forma de reutilizacin usando una acepcin ms amplia del trmino, se suelen englobar bajo el concepto comn de reutilizacin. As haremos nosotros.

REUTILIZACIN CON COMPOSICIN


Composicin La forma ms simple de reutilizar una clase es simplemente haciendo una nueva clase que la contenga. Esta es una tcnica llamada composicin, anterior a la POO, como se muestra en el ejemplo que sigue en Pascal:
type cFigura = record { declaracin de una figura } end; type cVectorFiguras = record V : array [1..100] of cFigura; CantFiguras : Word end;

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 -

PROGRAMACIN ORIENTADA A OBJETOS

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.

REUTILIZACIN CON HERENCIA


Definicin y ejemplos La herencia es algo central en la POO. Adems, es la propiedad de la POO que ms colabora para lograr una alta reutilizacin del cdigo. Por la herencia, cada clase puede tener una clase ancestro y clases descendientes. Y tambin gracias a ella, cada clase hereda atributos y comportamiento de su clase ancestro. La idea es que haya familias de clases que compartan estructuras y comportamientos similares. La herencia parte de relaciones del tipo "es un", las mismas que se utilizan para todas las taxonomas de las ciencias. Por ejemplo, en Zoologa, las orcas y los caballos son mamferos, por lo que podramos decir que heredan, de la clase ancestro de los mamferos, atributos (poseen mamas, sangre, etc.) y comportamiento (gestan hijos en su seno, los amamantan en los primeros das de vida, etc.). Lo mismo podramos decir en Matemtica de los enteros y reales como subclases de nmeros. Insisto, esto es factible por la relacin "es un": una orca es un mamfero, un real es un nmero. Veamos algunos ejemplos de taxonomas usuales provenientes de distintas ciencias:

- 37 -

CARLOS FONTELA

Taxonoma de animales cAnimal

cVertebrado

cInvertebrado

cMamifero

cAve

cPez

cMolusco

cCrustaceo

cElefante

cMurcielago

cCaracol
Taxonoma de idiomas cIdioma

cCalamar

cMonosilabico

cAglutinante

cIndoeuropeo

cSemitico

cLatino

cGermanico

cEslavo

cIndoiranio

Taxonoma de nmeros cComplejo

cReal

cImaginario

cRacional

cIrracional

cEntero

cFraccionario

cEnteroNegativo

cNatural

- 38 -

PROGRAMACIN ORIENTADA A OBJETOS

Taxonoma de figuras cFigura

cElipse

cCuadrilatero

cTriangulo

cOtras

cCircunferencia

cParalelogramo

cTrapecio

cRectangulo

cAcutangulo

cObtusangulo

cRectangulo

cRombo

cCuadrado

De un anlisis de las figuras anteriores, podemos deducir: ?? Las clasificaciones no tienen por qu ser completas, pero s excluyentes. ?? Algunas de las clases del rbol pueden no tener instancias: son llamadas clases abstractas (las veremos en el prximo captulo). ?? La ubicacin de una clase en la jerarqua se establece por la relacin es un. ?? Cada clase hereda comportamiento y estructura de su ancestro. Utilizacin de herencia Ante todo, digamos que no se debe confundir herencia con instanciacin, pues la primera es una relacin entre clases y la segunda entre una clase y un objeto de la misma. Tampoco debe confundirse con la composicin: en un caso se trata de una relacin contiene y en el otro de una relacin es un. En cualquier lenguaje de programacin que soporte POO, cuando se declara una clase descendiente se entiende que esta clase hereda de su ancestro inmediato todos sus atributos y todos sus mtodos, en forma implcita. Adicionalmente, la clase hija puede agregar atributos y mtodos propios, que a su vez sern heredados por sus descendientes junto con lo que ella hered de sus ancestros. Por ejemplo, tomando el caso de las figuras geomtricas ya mostrado, si se define un atributo Color en cFigura, ste atributo va a estar implcitamente definido (sin necesidad de declararlo) en cElipse, cCuadrilatero y dems. Del mismo modo, un mtodo Mover definido en cFigura tambin lo estar en todos sus descendientes. La nica excepcin es cuando se redefine un mtodo en una clase descendiente, que oculta al del mismo nombre de la clase ancestro (este tema va a ser analizado en mayor profundidad en el prximo captulo). Como dijimos ms arriba, la herencia es una herramienta imprescindible para la reutilizacin del cdigo. Martin y Odell [10] afirma n que con orientacin a objetos se reutiliza en promedio el 80% del cdigo. Hagamos la analoga en programacin modular tradicional: supongamos que tenemos declarados en una unidad de biblioteca o paquete el TDA cCuadrilatero y queremos trabajar con un tipo cTrapezoide. Sin POO, lo nico que podremos hacer es declarar el tipo necesario y luego rescribir los subprogramas que manejen ese tipo, copindolos de los de cCuadrilatero y adaptndolos. Parece un trabajo bastante pesado. Pero eso no es todo. Y si no se cuenta con el cdigo fuente del TDA, por ejemplo, porque est en una biblioteca compilada comprada? En este caso se debern escribir los subprogramas, pero desde cero, sin ningn tipo de ayuda. Parece ser bastante difcil lograr que dichos subprogramas se comporten de manera similar a los de la biblioteca: uno no puede saber qu consideraciones pasaron por la mente del implementador. Un paliativo puede ser utilizar composicin, pero eso no me librara de la necesidad de reescribir gran parte del cdigo, adems de utilizar un concepto incorrecto: un trapezoide no contiene un cuadriltero, es

- 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

Tratando de saber ms del problema de lo que s yo de familias idiomticas.

- 40 -

PROGRAMACIN ORIENTADA A OBJETOS

soportan interfaces, C++ no. Dejaremos el estudio de interfaces para ms adelante en este captulo. Herencia con excepciones A veces se necesitan definir excepciones a la herencia, en todas las disciplinas. Por ejemplo, tal vez queramos definir a las aves como animales voladores, con excepciones como los pinginos, las gallinas, etc.. Otro ejemplo puede ser definir a los idiomas que se hablan en Europa como indoeuropeos, con las excepciones del magiar, el fins y el turco. Y as sucesivamente. De todas maneras, el uso de herencia con excepciones es una prctica cuestionable, ya que tal vez se deberan utilizar subclases para expresar las excepciones. En los ejemplos anteriores, se podran clasificar los pases europeos en aquellos de lenguas indoeuropeas y aquellos de lenguas no indoeuropeas, o las aves en voladoras y no voladoras. De esta manera se corrige la inconsistencia lgica. Sin embargo, estas clasificaciones con subclases no permiten excepciones por diferentes categoras y elimina jerarquas que pueden ser interesantes. Por ejemplo, si deseo clasificar a las aves en voladoras y no voladoras, no voy a poder clasificarlas tambin como americanas y europeas sin caer en la herencia mltiple. De all que la herencia con excepciones tenga sus defensores, por lo menos en el terreno terico. Especializacin con menos atributos Ya dijimos que la herencia se debera dar entre clases cuando se les pueda aplicar la relacin "es un". Si no fuera as, la herencia de atributos y comportamiento se puede hacer caprichosa y sin sentido. Sin embargo, a veces estamos tentados de plantear excepciones a esta regla. Por ejemplo, un implementador de una aplicacin que dibuje figuras geomtricas se puede sentir tentado de declarar a la clase cElipse como descendiente de la clase cCircunferencia , agregndole, como atributo, un radio ms; sin embargo, esto viola el principio bsico de la herencia, ya que una circunferencia, aunque tenga un atributo menos, es una elipse y no lo contrario. Pero, cmo hacemos para declarar una subclase que tiene menos atributos que su ancestro? Algo parecido pasara con clases para definir matrices y matrices cuadradas (ver el ejercicio que se presenta ms adelante en este captulo). Los ejemplos que acabamos de ver son muy comunes en POO. Pero este problema no se ha resuelto en ningn lenguaje de programacin orientado a objetos, y tal vez su solucin sea una de las futuras mejoras en las tcnicas de desarrollo de software. La nica manera de representar una clase con menos atributos que su ancestro es hacer que ningn mtodo acceda al atributo en cuestin, y simultneamente hacerlo privado. Tambin podra pensarse en evitar la herencia de mtodos. En este campo, la solucin puede estar en la redefinicin (que veremos luego). Constructores, destructores, herencia y composicin Para asegurar que toda la asignacin de memoria e inicializacin se hace en la forma correcta, conviene, dentro de un constructor, llamar al principio al constructor de la clase ancestro inmediato, y dentro de un destructor, llamar al final al destructor de la clase ancestro inmediato. En Object Pascal:
constructor cClase.Create; begin inherited Create; { ... } end;

- 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

Salvo cuando haya ms de un destructor o cuando se precise pasarle parmetros.

- 42 -

PROGRAMACIN ORIENTADA A OBJETOS

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 m ediante 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 c lase cArticulo, que incluye libros, y que tiene un
27

En Java tambin podran ser friendly.

- 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 -

PROGRAMACIN ORIENTADA A OBJETOS

ELECCIN DE IMPLEMENTACIN: COMPOSICIN, HERENCIA O INTERFACES?


Muchas de las personas que se enfrentan por primera vez con la POO terminan creyendo que la herencia debe usarse siempre que sea posible. Pero la verdad es que siempre hay que analizar si en un determinado caso no conviene la composicin, que es sencilla de implementar y ms flexible. Por ejemplo, parecera obvio que una pila que se implementa con arreglos debera contener un vector como uno de sus atributos. Sin embargo, la implementacin de pilas de Java 1.0/1.1 est construida como una subclase de Vector. Esto es tan contraproducente que permite que se usen todos los mtodos de Vector para trabajar con pilas. En general, se usa composicin cuando se necesitan algunos aspectos de la clase contenida, pero no su comportamiento. De esta manera, se podr usar la funcionalidad de la clase contenida, pero a travs de la interfaz de la clase contenedora. En cambio, si se quiere tener la misma interfaz de la clase contenida, se debera usar herencia. Dicho de otro modo, la herencia se usa cuando una interfaz parece apropiada para incorporar ms comportamiento o distinta funcionalidad (una nueva versin). La herencia fue pensada ms bien para permitir el desarrollo incremental, para lo cual es una herramienta mucho ms poderosa que la composicin. Histricamente, la construccin de jerarquas de clases se ha hecho siguiendo bsicamente dos principios. El primero de ellos es el de la relacin es un, tambin llamado principio de sustitucin28 . Es el que hemos usado siempre en este curso, por ser el ms estable y recomendable, garantizar mejor la reutilizacin y extensin y el que utilizan las ciencias para sus taxonomas. En base a este principio se ubica un objeto en su clase, o una clase en una jerarqua, mediante pruebas de reconocimiento. Tambin se la llama herencia conceptual. El otro es el de la relacin es parte de. ste es menos recomendable. Si bien no es tcnicamente una herencia, parecindose ms a la composicin, a veces se la llama herencia de implementacin. Sera bueno dejar este caso para resolverlo con composicin y no con herencia. Hemos dicho que las interfaces solucionan la mayor parte de los problemas que se implementan con herencia mltiple, y de una forma ms elegante y simple. Sin embargo, tampoco es cuestin de forzar las interfaces para lograr herencia mltiple, como muchos autores han pretendido. Tambin la composicin es un buen instrumento para evitar la herencia mltiple en muchos casos.

IMPLEMENTACIN DE REUTILIZACIN DEL CDIGO EN DISTINTOS LENGUAJES


Composicin, herencia e interfaces en Object Pascal En Object Pascal, todas las clases declaradas con la palabra class que no se declaran con un ancestro inmediato tienen una clase ancestro por omisin llamada TObject. En ese ancestro por omisin estn definidos un constructor Create y un destructor Destroy, que deben redefinirse en cada descendiente siguiendo las recomendaciones que se mencionaron ms arriba29 . Adems, el modelo de memoria de TObject es tal que los objetos instancias de esta clase y sus descendientes se 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 hay que invocar al constructor que, adems de lo que indique su cdigo, asignar un lugar de memoria para el objeto
28 29

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.

- 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 31

Aunque en Delphi est bastante optimizado. Por ejemplo, si escribimos A := B, siendo A y B referencias o punteros, A queda apuntando a la misma direccin de B, por lo que en realidad son la misma variable. Lo mismo ocurre con parmetro y argumento si llamamos a un subprograma P(B) y B es un puntero o referencia. 32 Esto es as desde Delphi 6. Hasta Delphi 5 se usaba IUnknown como ancestro de ltima instancia de las interfaces.

- 46 -

PROGRAMACIN ORIENTADA A OBJETOS

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); }

En el caso de los constructores, la llamada a super se hace sin indicacin de mtodo:


super();

Como ya dijimos, Java define un constructor por omisin para cada clase. Adems, ste invoca
33 34

Aunque en casi todas las implementaciones de Java est muy optimizado. Por ejemplo, si escribimos A = B, siendo A y B referencias o punteros, A queda apuntando a la misma direccin de B, por lo que en realidad son la misma variable. Lo mismo ocurre con parmetro y argumento si llamamos a una funcin P(B) y B es un puntero o referencia. 35 Salvo que se usen trucos muy sucios en el cdigo.

- 48 -

PROGRAMACIN ORIENTADA A OBJETOS

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.

EJERCICIO RESUELTO: IMPLEMENTACIN DE MA TRICES


Enunciado Escribir clases que implementen matrices y matrices cuadradas, incluyendo todas las operaciones posibles. Las operaciones que generen matrices nuevas (suma, producto, etc.) no deben construir la matriz resultado, que se supone creada en el mdulo invocante. Solucin en Object Pascal Ver apndice A: Implementacin de matrices Solucin en C++ Ver apndice B: Implementacin de matrices Solucin en Java Ver apndice C: Implementacin de matrices

- 49 -

CARLOS FONTELA

EJERCICIO RESUELTO: USO DE LA IMPLEMENTACIN DE MATRICES


Enunciado Escribir un subprograma que, utilizando la implementacin de matrices del ejercicio anterior, resuelva un sistema de ecuaciones lineales del tipo: A.X=B, haciendo uso de la solucin X=A -1.B. Solucin en Object Pascal Ver apndice A: Uso de la implementacin de matrices Solucin en C++ Ver apndice B: Uso de la implementacin de matrices Solucin en Java Ver apndice C: Uso de la implementacin de matrices

- 50 -

PROGRAMACIN ORIENTADA A OBJETOS

5. PROGRAMACIN ORIENTADA A OBJETOS: POLIMORFISMO


POLIMORFISMO
Polimorfismo y vinculacin tarda Polimorfismo quiere decir "muchas formas". En POO llamamos polimorfismo a la propiedad que tienen los mtodos de mantener una respuesta unificada, con la misma semntica, aunque con distinta implementacin, a travs de la jerarqua de clases. O dicho de otra manera, la llamada a una misma operacin puede invocar a mtodos distintos. A veces se lo denomina abstraccin de mensajes. En definitiva, lo que logra el polimorfismo es retardar la decisin sobre el tipo (o clase) del objeto hasta el momento en que vaya a ser utilizado el mtodo. En este sentido, el polimorfismo est asociado a lo que se denomina vinculacin tarda o vinculacin en tiempo de ejecucin. La idea es que los objetos de distintas clases derivadas de otra puedan ser tratados de la misma manera, y se le apliquen los mismos mtodos, aunque las imp lementaciones particulares sean diferentes. Esto va a permitir una mayor separacin de interfaz e implementacin y va a dar una nueva ayuda en pos de la extensin del cdigo. Por qu? Porque una vez que se escriba un mtodo para una clase, la creacin de clases derivadas no va a implicar redefinirlo cuando tenga una semntica similar. Como vemos, el polimorfismo es un concepto complejo, y muy ligado a la herencia y el encapsulamiento. Por eso, vamos a empezar estudiando algunas propiedades que se asocian al polimorfismo para luego llegar a un anlisis del mismo. Redefinicin36 La redefinicin es una funcionalidad de la POO que se usa cuando el comportamiento de la clase ancestro no es exactamente igual para la descendiente. Por ejemplo, en el caso de las fig uras geomtricas que introdujimos en el captulo anterior, el mtodo Dibujar puede no ser igual para todas las clases, aunque todas dibujen una figura. En ese caso, tendramos un Dibujar para cFigura, otro para cElipse, otra para cCirculo, etc.. Simplemente, esto se resuelve escribiendo diferentes mtodos Dibujar, y decimos que el mtodo Dibujar de una clase descendiente redefine al mtodo Dibujar de su ancestro. Esta redefinicin se debe a que ahora, cada vez que se invoque al mtodo Dibujar para una instancia de cCirculo nos estaremos refiriendo, no ya al que se hubiera heredado de cFigura o de cElipse, sino al redefinido en cCirculo . Por lo tanto, la llamada a una misma operacin depender de la clase a la que pertenece el objeto. Por ejemplo 37 :
UnaElipse.Dibujar(); UnCirculo.Dibujar(); UnCuadrado.Dibujar();

Todos se refieren a la operacin Dibujar, pero en realidad estn invocando a implementaciones diferentes, segn la clase del objeto para el cual se llama al mtodo.
36

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 p armetro38. 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 -

PROGRAMACIN ORIENTADA A OBJETOS

ejemplo, estas lneas de cdigo son vlidas en Object Pascal:


type cClaseDerivada = class(cClaseBase) ... procedure P (X:cClaseBase); ... var UnObjetoClaseDerivada : cClaseDerivada; ... P(UnObjetoClaseDerivada);

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()

Si el mtodo Mover de cFigura se puede escribir (en Object Pascal):


procedure cFigura.Mover (X,Y:Word); begin
el argumento es parte del mtodo invocante, y el parmetro del invocado.

- 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, c omo 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 -

PROGRAMACIN ORIENTADA A OBJETOS

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.

CLASES Y MTODOS ABSTRACTOS


Clases abstractas A veces es necesario definir clases para las cuales no tiene sentido crear instancias. Un caso podra ser nuestra ya habitual cFigura, dado que las clases del nivel inmediato inferior cubren todos los casos posibles. Es decir, una figura en particular ser cElipse, cCuadrilatero o cOtros, pero nunca directamente una cFigura. En cualquier taxonoma ocurre lo mismo.
39

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 -

PROGRAMACIN ORIENTADA A OBJETOS

USO DE INFORMACIN DE TIPOS EN TIEMPO DE EJECUCIN


Transformacin de tipos En la mayor parte de los lenguajes de programacin tradicionales existe alguna forma de transformar un valor de un tipo en otro. Pero si bien era algo que poda hacerse en todos los lenguajes, pocas veces era realmente necesario utilizarlo. Los programadores C saben de esto, y de cmo se puede hacer difcil de probar y de mantener un programa en el que se hace a buso de esta capacidad del lenguaje (afortunadamente, C++ es ms restrictivo que C, no permitiendo convertir arbitrariamente tipos muy diferentes). Por ejemplo, en C, la lnea:
c = (char) x;

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);

Podemos invocarlo as:


P(UnaElipse);

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

La notacin es de Object Pascal.

- 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;

permitiran en el bloque de cdigo en que se hayan declarado, las siguientes acciones:


V[1] := UnaElipse; V[2] := UnCirculo; V[3] := UnTrianguloRectangulo;

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 -

PROGRAMACIN ORIENTADA A OBJETOS

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.

IMPLEMENTACIN DEL POLIMORFISMO EN DISTINTOS LENGUAJES


Polimorfismo y otros aspectos en Object Pascal En Object Pascal la redefinicin se hace simplemente reescribiendo el mtodo en cuestin en la clase derivada. La sola declaracin de un mtodo del mismo nombre que otro de la clase ancestro provoca su ocultamiento. Si se desea sobrecargar el mtodo de la clase ancestro debe declararse explcitamente con la directiva overload. Los mtodos virtuales se deben declarar explcitamente, con las siguientes reglas: ?? En la primera clase en la cual se declara un mtodo virtual, viniendo de arriba hacia abajo en la jerarqua, se hace poniendo las palabras virtual o dynamic41 a continuacin de la declaracin del encabezado del mtodo. ?? En las clases subsiguientes de la jerarqua se coloca la palabra override. Por ejemplo, en la clase cFigura, el mtodo Dibujar se encabezara:
procedure Dibujar; virtual; { puede usarse dynamic }

Mientras en cElipse y cCirculo sera:


procedure Dibujar; override;

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 -

PROGRAMACIN ORIENTADA A OBJETOS

una elipse. Para evitar este inconveniente se puede utilizar el operador as:
(UnaFigura as cElipse).Dibujar;

La informacin sobre el tipo se puede obtener con el operador is:


if UnaFigura is cElipse then (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

Polimorfismo y otros aspectos en Java En Java 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 mtodo de la clase ancestro. Todos los mtodos son virtuales porque todo en Java se hace con vinculacin dinmica, salvo que el mtodo se declare especficamente con la palabra final o sea privado (recordar que los mtodos privados no pueden redefinirse). Los mtodos abstractos se especifican anteponindoles la palabra abstract. Se declaran como en el ejemplo:
abstract public void dibujar ();

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 -

PROGRAMACIN ORIENTADA A OBJETOS

EJERCICIO RESUELTO: PROPIEDADES DE UNA INMOBILIARIA


Enunciado Escribir clases para manejar las propiedades en venta de una inmobiliaria: casas, departamentos y terrenos baldos. Debe haber subprogramas que calculen la comisin del vendedor a partir del precio de venta, sabiendo que ste tiene frmulas de clculo distintas para casas, departamentos y terrenos. La unidad debe ser autosuficiente y estar correctamente modularizada. Solucin en Object Pascal Ver apndice A: Propiedades de una inmobiliaria Solucin en C++ Ver apndice B: Propiedades de una inmobiliaria Solucin en Java Ver apndice C: Propiedades de una inmobiliaria

EJERCICIO RESUELTO: USO DE LA IMPLEMENTA CIN DE LA INMOBILIARIA


Enunciado Escribir un subprograma que, utilizando la implementacin de la unidad del ejercicio anterior, compare dos propiedades (que podrn ser casas, departamentos o terrenos baldos) e imprima los datos de la de mayor precio de venta. Solucin en Object Pascal Ver apndice A: Uso de la implementacin de la inmobiliaria Solucin en C++ Ver apndice B: Uso de la implementacin de la inmobiliaria Solucin en Java Ver apndice C: Uso de la implementacin de la inmobiliaria

- 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 q he escrito con ue Pablo Surez, que se puede descargar de http://www.fi.uba.ar/materias/7507F/.

UML PARA DOCUMENTAR LA PROGRAMACIN


Una de las tantas formas de documentar la programacin consiste en utilizar diagramas que muestren en forma grfica distintos aspectos de la aplicacin. UML Como la falta de un estndar que fijara el camino para la modelizacin de software orientado a objetos creaba confusin entre los desarrolladores, entre 1994 y 1995, Booch, Rumbaugh y Jacobson ? tres de los ms reconocidos metodologistas? decidieron unirse a Rational Software Corporation para tratar de elaborar ese estndar. La idea fue rescatar las caractersticas ms convenientes de cada una de las corrientes disponibles, unificarlas y extenderlas bajo el nombre de UML (Unified Modeling Language). La versin 1.0 de

- 64 -

PROGRAMACIN ORIENTADA A OBJETOS

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 t mbin al modelado de las partes no a 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 48

El concepto de colaboracin lo veremos en un captulo prximo, y no lo diagramaremos. Aqul cuyo valor depende del de otros atributos. 49 Las tarjetas CRC se usan para el modelado de clases en las primeras fases del desarrollo.

- 66 -

PROGRAMACIN ORIENTADA A OBJETOS

cPedido * 1 1

cCliente

+Artculos de lnea

CLineaDePedido

Los grados de multiplicidad vlidos son: Grado de Multiplicidad 1 0..1 M..N (indicando M y N como nmeros) * 0..* 1..* M..* 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 +Depositar() +Retirar()

cCuentaCorriente +Depositar() +Retirar()

cCAComun +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 +Depositar() +Retirar()

cCuentaCorriente +Depositar() +Retirar()

cCAComun +Depositar() +Retirar()

cCAEspecial +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 1 * cPunto

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 -

PROGRAMACIN ORIENTADA A OBJETOS

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 -/ Empleador

cPersona 1 0..1

cEmpleo -aPeriodo : Date * 1

cEmpresa

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

Finalmente, debe tenerse en cuenta para los diagramas de clases: ?? No es necesario representar todas las clases con t dos sus detalles, sino slo lo que sea o necesario. ?? En general, conviene representar clases, asociaciones, atributos y generalizacin. La visibilidad, las asociaciones mltiples y otras propiedades son necesarias slo en algunas ocasiones especiales. ?? Convie ne dibujar modelos slo de las partes importantes del sistema y mantenerlos siempre actualizados entre prototipos y versiones. ?? Son herramientas, por lo que el grado de detalle hay que manejarlo desde el punto de vista de la practicidad. ?? No hay que representar las clases de apoyo. Por ejemplo, si para resolver un problema de simulacin necesito, como herramienta matemtica, un vector de posicionamiento en 3 dimensiones, esto no debera figurar porque no hace al espacio del problema. ?? Conviene ordenar los elementos del diagrama para minimizar el cruce de lneas. ?? Conviene ordenar los elementos del diagrama para que las cosas que son cercanas semnticamente queden cerca fsicamente. ?? Conviene usar colores o seales visuales para sealar caractersticas importantes del diagrama.

- 69 -

CARLOS FONTELA

?? Los diagramas de clases deben tender a mostrar no ms de un tipo de relacin en forma preponderante. As, se harn diagramas que enfaticen las relaciones de herencia separados de aqullos que enfaticen asociaciones o colaboraciones. Diagramas de secuencia Los diagramas de secuencia son modelos dinmicos que describen cmo colabora un grupo de objetos en un determinado escenario51 a travs del tiempo. En ellos se representan objetos y no clases. En UML el nombre de objeto se subraya. Si no se conociese el nombre de un objeto pero s la clase a la que pertenece, se puede representar con dos puntos seguido del nombre de clase, subrayado. Otras veces se puede escribir tanto el nombre del objeto como el de la clase. En el diagrama de secuencia, un objeto se representa como un rectngulo arriba de su lnea de vida. Los mensajes son flechas entre objetos y el orden de los mensajes est dado por el eje vertical, que se lee de arriba hacia abajo. De los mensajes se escribe, por lo menos, el nombre, aunque tambin se pueden agregar los parmetros e informacin de control: iteraciones, condiciones, etc.. A continuacin se muestra un diagrama de secuencia de un pedido de saldo en un cajero automtico:

Usuario XX

Cajero YY

DatosUsuarios

DatosCuentas

IngresaTarjeta()

VerificarTarjeta() PedirClave() ChequearUsuario (Tarjeta,Clave)()

<<create>> InicioTransaccion (Usuario)() Transacc ZZ MostrarMenu()

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 -

PROGRAMACIN ORIENTADA A OBJETOS

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 mensaj InicioTransaccion. Las creaciones se suelen e 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 mi mo (se suicida), o puede ser s 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 h con el izo 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

Jaque Ganan las negras

Juegan las blancas

Juegan las negras

Tablas

Turno de las negras

Jaque mate

Ganan las blancas

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 -

PROGRAMACIN ORIENTADA A OBJETOS

el menos orientado a objetos de los diagramas de UML, pues se centra en procesos. Sin embargo, suele ser til para ver el flujo de informacin en sistemas en que este flujo es importante. Por lo tanto, tambin es interesante para ver el paralelismo entre las tareas, aunque luego no se programen como tareas concurrentes. Por su naturaleza, tambin se lo suele aplicar al modelado de las partes no informticas de un sistema, como flujos de documentos, personas y productos. A continuacin se muestra un diagrama de actividades simplificado de la extraccin de dinero de un cajero automtico:

- 73 -

CARLOS FONTELA

Cliente

Cajero

Banco

Inserta tarjeta

Pide clave

Ingresa clave

Chequea clave

[clave incorrecta] [clave correcta]

Pide monto Chequea saldo

Procesa transaccin

[saldo >= monto]

Retira dinero

Entrega dinero

Debita cuenta

[saldo < monto] Muestra saldo

Retira tarjeta

Expulsa tarjeta

- 74 -

PROGRAMACIN ORIENTADA A OBJETOS

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 este modo, aparecer un vnculo del tipo vase tambin. La informacin de versin de una clase o paquete se puede indicar con el comando @version :
@ version informacin-de-versin

La informacin a colocar puede ser cualquiera que nos parezca interesante.


52

De todas maneras, se puede incluir los atributos y mtodos privados agregando private en la lnea de comandos.

- 76 -

PROGRAMACIN ORIENTADA A OBJETOS

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

Tambin se pueden incluir en la documentacin avisos de supresin de una determinada caracterstica en versiones futuras, de modo de que su uso vaya siendo abandonado, con el comando @deprecated, que provoca un aviso del compilador. Estndares de nomenclatura Como ocurri siempre en la historia de la programacin, sigue siendo vlida la idea de usar estndares de nomenclatura, de modo tal que mirando un identificador en medio del cdigo podamos determinar si se trata de una clase, de un mtodo, etc.. Tambin deberamos poder deducir su significado. Hay diferentes tipos de estndares que intentar lograr este propsito. Uno de ellos es el mtodo hngaro, tal vez el ms recargado, que llega a colocar una letra como prefijo para indicar el tipo y sufijos que indican quin es el autor. Tambin recomienda empezar algunos nombres con _. Lamentablemente, todo esto hace que los programas no sean demasiado legibles. Suele estar desaconsejado, y se lo pone de ejemplo de exageracin. El estndar de nomenclatura ms usual para Java es el que sigue: ?? Para las clases, utilizar nombres extrados del vocabulario del sistema, y escribirlos en minsculas, utilizando las maysculas al comienzo de cada palabra, como en NumeroComplejo o TrianguloRectangulo. ?? Para las interfaces, usar las mismas convenciones que para las clases, agregando una I al comienzo. ?? Para los atributos, se suelen utilizar nombres de propiedades de la clase a la que pertenecen, escribindolos en minsculas, salvo para separar palabras, como en parteReal o hipotenusa. ?? Para los mtodos, se suelen elegir verbos en infinitivo o participios, con la misma convencin de maysculas y minsculas que para los atributos. ?? Para los objetos, se aplican los mismos criterios que para las clases, con la posibilidad de ser ms precisos, utilizando la convencin de maysculas y minsculas de los atributos y mtodos. En Pascal se utiliz durante mucho tiempo un estndar que consista en escribir todo en minscula y separar palabras con guin bajo (_). Luego, y a pesar de que el compilador Pascal no distingue, se empez a
53

El concepto de excepcin se ve en un captulo posterior.

- 77 -

CARLOS FONTELA

usar este estndar, que luego qued en Object Pascal: ?? Para los tipos y clases, utilizar nombres extrados del vocabulario del sistema, y escribirlos en minsculas, utilizando las maysculas al comienzo de cada palabra y agregando una T delante, como en TCanvas y TComponent. ?? Para las interfaces se usan las mismas convenciones que para las clases, pero con una I en vez de la T. ?? Para los atributos, utilizar nombres de propiedades de la clase a la que pertenecen, escribindolos con las mismas convenciones de las clases, sin ninguna letra inicial. ?? Para los mtodos, elegir verbos en infinitivo o participios, con la misma convencin que para los atributos. ?? Para los objetos, la misma notacin de los mtodos y atributos en cuanto a la convencin de maysculas y minsculas. En nuestro caso, hemos recurrido a un estndar consistente en: ?? Para las clases, utilizar nombres extrados del vocabulario del sistema, y escribirlos en minsculas, utilizando las maysculas al comienzo de cada palabra. Para enfatizar que se trata de una clase, hemos agregado una c adelante, como en cComplejo. ?? En las clases de excepcin hemos reemplazado la c por una e. ?? Para las interfaces, us amos las mismas convenciones que para las clases, pero con una i minscula en vez de la c. ?? Para los tipos que no son clases, utilizamos una t delante del nombre. ?? Para los atributos, utilizamos nombres de propiedades de la clase a la que pertenecen, escribindolos con las mismas convenciones de las clases, salvo que la primera letra es siempre una a. ?? Para los mtodos, elegir verbos en infinitivo o participios, con la misma convencin que para los atributos y clases, pero sin colocar ninguna letra delante. ?? Para los objetos, aplicamos la misma notacin de los mtodos en cuanto a la convencin de maysculas y minsculas. Nos pareci que este estndar de nomenclatura era adecuado desde el punto de vista del cliente, que suele tratar con objetos y mtodos ms que con clases y atributos. Asimismo, nos pareci que cumpla una finalidad didctica mayor que los anteriores. De todas maneras, no importa demasiado qu estndar se utilice, sino que se utilice alguno, y sobre todo, con fundamentos y coherencia. Por otro la do, como el lenguaje que se est utilizando siempre tiene identificadores predefinidos, habra que tratar de que nuestro propio estndar no difiera demasiado del del lenguaje.

EJERCICIO RESUELTO: DIAGRAMAS DE ACTIVIDADES, CLASES Y SECUENCIA


Enunciado Hacer los diagramas de actividades (a nivel de anlisis), de clases y de secuencia del proceso de anotacin de un alumno en las materias de un postgrado. Para ello, se supondr que cada alumno podr anotarse en varios cursos, para los cuales habr que verificar que tenga aprobadas las correlativas. En una segunda etapa, el profesor del curso y el coordinador del postgrado evaluarn la situacin del alumno y aprobarn o no la solicitud, dado que pueden considerar que la orientacin general que el aspirante le est dando a sus estudios no debera orientarlo en esa direccin.

- 78 -

PROGRAMACIN ORIENTADA A OBJETOS

Solucin Diagrama de actividades:

- 79 -

CARLOS FONTELA

Aspirante

Sistema inscripcin

Profesor

Coordinador

Solicita ingreso sistema inscripciones

Pide datos y clave

Ingresa datos y clave

Chequea clave

[clave incorrecta] [clave correcta]

Pide inscribirse en curso Chequea correlativas

[correlativas insuficientes]

[correlativas suficientes]

Evuala situacin

Evuala situacin

[solicitud no aprobada]

[solicitud no aprobada]

Recibe rechazo

[solicitud aprobada]

[solicitud aprobada]

Recibe aprobacin solicitud

Notifica alumno

Inscribe alumno

- 80 -

PROGRAMACIN ORIENTADA A OBJETOS

Diagrama de clases:
1

cPersona * 1

cSistemaInscripcion +PantallaInscripcion() +ElegirCurso()

cCurso +ChequearCorrelativas() +InscribirAlumno() *

cAspirante +NotificarAlumno()

cDocente +ConsultarOpinion()

1 cProfesor +ConsultarOpinion() cCoordinador +ConsultarOpinion()

cCurriculaAlumno +ConsultarCurricula() 1

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.

EJERCICIO RESUELTO: UN DIAGRAMA DE ESTAD OS


Enunciado Hacer el diagrama de estados - transiciones para el siguiente escenario: a) Un reloj digital tiene un visor y dos botones, el botn A y el botn B. El reloj tiene dos modos de operacin, mostrar hora y corregir hora. b) En el modo mostrar hora, se muestran las horas y minutos, separados por dos puntos. El modo corregir hora tiene dos submodos, corregir hora y corregir minutos. c) El botn A es el usado para cambiar de modo. Cada vez que es presionado el modo avanza secuencialmente: mostrar hora, corregir hora, corregir minutos, etc. d) En cada submodo el botn B es usado para avanzar las horas o minutos cada vez que es presionado. Los botones deben ser soltados antes de generar algn otro evento.

- 82 -

PROGRAMACIN ORIENTADA A OBJETOS

Solucin
Mostrando hora

[Botn A apretado]

[Botn B apretado] / Horas++ Corrigiendo horas [Botn A apretado]

[Botn B apretado] / Minutos++ Corrigiendo minutos

- 83 -

Potrebbero piacerti anche