PROGRAMACIÓN ORIENTADA A OBJETOS EDUCATIVA EN UNA MÁQUINA VIRTUAL BASADA EN PROTOTIPOS J. Baltasar García Perez-Schofield1, Emilio García Roselló1, David Martínez Torres2, Manuel Perez Cota1 1 Departamento de Informática, Edif. Fundición, s/n, Campus As Lagoas-Marcosende, 36201 Vigo, España. {jbgarcia | mpcota | erosello}@uvigo.es 2 Mixteca. Huajuapan de León. 69000 Oaxaca - México. dtorres@mixteco.utm.mx La programación orientada a objetos es una las especialidades más importantes a estudiar dentro del campo de las ciencias computacionales. El paradigma de orientación a objetos está ya absolutamente asentado en nuestros planes de estudio, de manera que hoy en día se enseña antes incluso que el paradigma estructurado. De una manera generalizada, el lenguaje que se está adoptando para la enseñanza es Java. Si bien este lenguaje es perfectamente válido, existen otros lenguajes, que siguen otros modelos de orientación a objetos que podrían también ser aplicables e incluso presentar algunas ventajas. En este artículo, se presenta precisamente una máquina virtual diseñada con el objetivo de servir especialmente para la enseñanza, que presenta una naturaleza distinta a la de aquellos lenguajes basados en clases (C++, Java), y con sus propias ventajas para la enseñanza. 1. Introducción A lo largo de los últimos años, ha habido varios intentos de crear plataformas de aprendizaje para la correcta asimilación de los conceptos de programación. Uno de los más llamativos y con más éxito, es probablemente, BlueJ (Kölling et al., 2003). Si bien BlueJ hace más énfasis en un entorno de programación que en un lenguaje de programación, este proyecto supuso en el pasado el desarrollo, efectivamente, de un lenguaje que aportara buena parte de las características que presenta Java (si bien Java es descartado en un principio, aunque favorablemente evaluado en (Kölling, 1999a)). Este lenguaje era Blue (Kölling, 1999b). El proyecto Blue supuso en su momento un estudio de varias características que un lenguaje de programación debería tener para ser considerado adecuado para educación (Kölling, 1999a). Es obvio que este estudio está motivado respecto al aprendizaje temprano (primeros cursos) del paradigma de programación orientada a objetos. Si bien los autores encuentran muy razonables algunas de las características pedidas por Kölling y su equipo para un lenguaje orientado a objetos educativo (por ejemplo "pequeño y simple", "alto nivel", "sintaxis legible", "no redundancia de conceptos", "pureza" ...), con uno especialmente los autores de este trabajo muestran su desacuerdo: "seguridad respecto al tipo". Con este último, los autores del proyecto BLUE tratan de proporcionar mayores ventajas al programador novato. Si bien la corrección estática y el fuerte tipado (aplicables a los lenguajes que pueden considerarse estáticos, como C++ (Stroustrup, 1991) y Java (Naughton, 1996)) pueden ser características deseables, y tener importantes ventajas, la dinamicidad (cuando las comprobaciones se hacen VII Congresso Iberoamericano de Informática Educativa 1347 tiempo de ejecución, como en Self, por ejemplo) puede tener también interesantes características. Los lenguajes dinámicos comienzan su andadura con Smalltalk (Goldberg & Robson, 1989). Pero todavía más relacionado con el sistema presentado aquí es Self (Smith & Ungar, 1987). Presentaba un modelo de orientación a objetos realmente nuevo en aquel momento: el denominado basado en prototipos. Su característica más destacada era la ausencia de clases. En realidad, este modelo estaba diseñado como un superconjunto del modelo de orientación a objetos basado en clases, mediante la definición de objetos prototípicos, que actuarían como modelos para la creación de nuevos objetos, mediante su clonación. Desgraciadamente, los lenguajes que implementan este modelo no son fácilmente accesibles. Algunos de ellos son dependientes de plataforma (como el mismo Self, de Sun); Smalltalk y también Self presentan una sintaxis que no es sencilla para muchos programadores (novatos y con experiencia, Kölling, 1999a); de hecho, el aprendizaje de este estilo de sintaxis es un tema merecedor de artículos dedicados. como (LaLonde, 2000). Otros lenguajes, como Kevo (que supone una curiosa mezcla entre un lenguaje basado en prototipos (dinámico por naturaleza), y un lenguaje estático, Tailvaisaari, 1992), han sido abandonados. Además de la simplicidad y la dinamicidad, los autores han identificado un tercer punto importante, como es el soporte de persistencia. El interés en esta característica, todavía no soportado totalmente en la máquina, se justifica en que suaviza muchísimo la curva de aprendizaje del paradigma de pogramación orientada a objetos, al no precisar que el programador distinga entre memoria secundaria y primaria (Kölling, 1999a), consistiendo en una capacidad natural de los objetos (como se perfila en García Perez-Schofield & Pérez Cota, 1998). De hecho, en este artículo se presenta una máquina virtual pequeña y simple (lo cual redunda en beneficios para la educación (Smith & Ungar, 1987)), sin soporte para tipos, orientada a objetos pura y basada en prototipos, llamada Zero1. Si bien los autores no pueden todavía presentar un lenguaje de programación de alto nivel sobre esta máquina, hay ya disponible un macroensamblador, correcto, gracias al alto nivel de abstracción inherente de la máquina, para programación de proyectos de tamaño medio. 2. Programación de la máquina virtual 2.1. Arquitectura de la máquina virtual Las características básicas de esta máquina virtual son las siguientes: 1Disponible en http://trevinca.ei.uvigo.es/~jgarcia/TO/zero/, con sus manuales correspondientes. VII Congresso Iberoamericano de Informática Educativa 1348 • herencia simple, dinámica (implementada mediante delegación) • creación y clonación de objetos (y prototipos, indistinguibles de los primeros) • paso de mensajes • manejo de excepciones • persistencia (no implementada todavía). La máquina virtual está basada en registros (que guardan referencias a objetos), estructurándose en dos grandes grupos: el acumulador (__acc), que guarda la referencia resultado de la instrucción anterior, el registro que guarda el objeto que está ejecutando el método, conocido como self en Java (__this), y el registro que guarda la excepción que se haya producido (__exc); y en un segundo grupo los registros generales que pueden ser utilizado para cualquier propósito (__gpn). 2.2. Compilación de objetos y paso de mensajes Los objetos se crean empleando object y endObject para marcar el comienzo y el fín del objeto, pudiendo crear en su interior atributos (attribute) y métodos (definidos entre method y endMethod). Los mensajes a métodos se realizan, básicamente, utilizando el mismo formato que Java, <nombreObjeto>.<nombreMétodo>. Es posible (de hecho, muy habitual) que entre el nombre del objeto y el nombre del método se sitúen varios nombres de atributos, que devuelven una referencia a un nuevo objeto, como en System.console.write(), donde console es un atributo de System. Un listado del típico programa (objeto) HolaMundo, que muestra por consola la cadena "¡Hola, Mundo!", se muestra a continuación. El objeto HolaMundo envía un mensaje (write()) al objeto de consola (_Zero_Console, accesible desde System) para enviar una cadena a la consola del sistema. object HolaMundo : ConsoleApplication method + doIt() parent.prepare() ! Preparar la e/s System.console.write("¡Hola, Mundo!") System.console.lf() return endMethod endObject El programa macroensamblador (zm, al que se le pasaría un listado como el anterior), traduce código fuente en macro enamblador para la máquina virtual Zero en su propio bytecode. La máquina virtual (zvm) ejecuta objetos que pueden, como en este caso, residir en un archivo con extensión ".zbj". VII Congresso Iberoamericano de Informática Educativa 1349 2.3. Encapsulación y creación de objetos No existe una división especial entre objetos y prototipos. Normalmente, si bien no tiene por qué ser así, los prototipos son objetos definidos en tiempo de compilación, mientras que los objetos son aquellos que se crean mediante la clonación de los primeros, en tiempo de ejecución. Sin embargo, existen otras posibilidades en tiempo de compilación, como clonar un objeto, eliminar o añadir atributos y métodos y emplearlo como prototipo, o incluso, existe la alternativa de crear un objeto vacío (sin métodos ni atributos), heredando de uno dado, pasandole el mensaje createChild(), y añadiendo los métodos y atributos necesarios. Los métodos y atributos, que se marcan con las palabras clave method y attribute, respectivamente, llevan un signo '+', si son públicos, o un signo '-' si son privados. En el caso concreto de los atributos, ni siquiera en el caso de ser públicos, estos pueden ser modificados desde un objeto que no sea el mismo que los posee. El clonado se realiza pasando el mensaje copy() a un objeto. Este mensaje conlleva un argumento que es el nombre del nuevo objeto, si bien puede pasarse una cadena vacío, delegando en el sistema la creación de un nombre para él. En un método pueden existir referencias locales a ese método (frente a las referencias que son locales a un objeto, los atributos), que se crean mediante la palabra clave reference. Las apariciones de enteros, flotantes y cadenas en el código fuente son convertidas, mediante el compilador, a objetos, clonados de LiteralInt, LiteralFloat y LiteralString, respectivamente. La creación del prototipo Punto, su clonado, y la manera de indicar la encapsulación se muestran a continuación: object Punto ! Nótese que el prototipo Punto ya es un objeto, por lo ! que es necesario dar valores a sus atributos. attribute + x = 0 attribute + y = 0 method + ponCoordenadas(a, b) x = a y = b return endMethod method + toString() reference toret = "(" toret = toret.concat(x.toString()) toret = toret.concat(", ") VII Congresso Iberoamericano de Informática Educativa 1350 toret = toret.concat(y.toString()) toret = toret.concat(")") return toret endMethod endObject object PruebaPunto : ConsoleAplication method + doIt() ! Crea un objeto reference miPunto = Punto.copy("") miPunto.ponCoordenadas(100, 150) ! Muéstralo por pantalla __this.prepare() ! Preparar la e/s System.console.write(miPunto) System.console.lf() return endMethod endObject La compilación y ejecución de PruebaPunto mostraría por pantalla: (100, 150) 2.4. Herencia La herencia soportada, es, como se ha visto, del tipo simple. Concretamente, existe un atributo especial (todos los objetos lo poseen, denominándose parent) que es aquel que denota al objeto padre de un objeto dado. Así, en el ejemplo anterior, PruebaPunto hereda de ConsoleApplication, lo que le provee automáticamente de la inicialización y el acceso a la consola, por ejemplo. Todos los objetos derivan del objeto Object, la raíz de la jerarquía de herencia. Este objeto provee de las operaciones básicas al resto de objetos, como son, por ejemplo, copy(), createChild(), isTheSameAs(), getName(), ... y otros. 2.5. Polimorfismo La propia naturaleza del lenguaje hace que cualquier paso de mensajes sea necesariamente polimórfico. Concretamente, la herencia se implementa mediante delegación (Smith & Ungar, 1987), y, así, cuando un objeto es incapaz de satisfacer un mensaje, lo reenvía a (delega en) su padre. Mientras un programador de Java lo encontraría natural (si bien en Java la herencia no se implementa mediante delegación), el programador de C++ encontrará que no es necesario indicar virtual delante de cada método que podría ser VII Congresso Iberoamericano de Informática Educativa 1351 necesario que soportara una llamada polimórfica. 2.6. Herencia dinámica La herencia dinámica es una ventaja que viene dada gratis por la propia simplicidad y naturaleza dinámica del sistema. Como ya se ha explicado, la herencia viene marcada por un atributo parent. Este atributo, como cualquier otro, puede ser cambiado por el propio objeto. La modificación controlada de este atributo proporciona la característica conocida como "herencia dinámica". En el siguiente ejemplo, un objeto contenedor (concretamente, un vector asociativo) cambia su padre según se cumpla una condición. El primero de ellos es el prototipo EmptyMap, empleado cuando no contiene elementos. Existe, así, el prototipo NonEmptyMap (no mostrado), que es el padre cuando se introducen elementos en el mismo. Este cambio de padre permite simplificar enormemente los métodos lookUp(), add() y delete() del objeto (respectivamente, buscar, añadir y borrar), puesto que no es necesario comprobar si existen elementos o no. object EmptyMap : Map method + lookUp(name) throw EObjectNotFound return endMethod method + delete(name) throw EObjectNotFound return endMethod method + add(name, obj) ! Lanzar un mensaje al padre, como "super" en Java __this.^add(name, obj) parent = NonEmptyMap return endMethod endObject Esta técnica no está exenta de problemas. La modificación, sin control, del atributo parent, puede llevar a un programa caótico. En el diseño del lenguaje de alto nivel, se contempla la creación de un mecanismo que permita controlar estas modificaciones. 3. Trabajo relacionado La particularidad que más desmarca a este sistema de programación de los demás es su futuro soporte de persistencia. Un sistema que presenta esta característica de una forma similar a la VII Congresso Iberoamericano de Informática Educativa 1352 deseada es PJama (Persistent Java) (Atkinson & Jordan, 2000). Sin embargo, este sistema implementa persistencia ortogonal (Atkinson & Morrison, 1995), mientras que, como se explica a continuación, la diseñada para Zero es sólo un subconjunto de esta última. De hecho, será implementada siguiendo el modelo de persistencia basado en contenedores (García Perez-Schofield et al., 2001), tal y como se empleó en Barbados (García PerezSchofield, 2002a). Este modelo es vagamente parecido al que presenta el middleware PerDis (Shapiro et al., 1997). Se trata, básicamente, de un particionamiento del almacenamiento persistente visible por el usuario, de manera que puede elegir en qué cluster desea guardar sus objetos. Por supuesto, ésto se recubre con una pequeña capa de abstracción, como un sistema de directorios en el caso de Barbados (García Perez-Schofield, 2002b). El resto de sistemas símplemente no poseen esta capacidad (como BlueJ, que empla Java), o su soporte de persistencia es muy primitivo, como en Self, que directamente guarda una imagen (el fichero es denominado image) de la memoria que permite que los objetos persistan en el entorno de trabajo (workspace) actual entre una ejecución y otra. Será necesario cambiar el fichero de imagen desde fuera de Self para disponer de varios workspaces o realizar todas las aplicaciones en un solo worksapce. Self (Smith & Ungar, 1987), es el sistema, en cuanto a dinamicidad, en el que se inspira Zero. Sin embargo, tanto el macro ensamblador como el lenguaje que se implemente en un futuro cercano no seguirán la sintaxis empleada por Self. Sí es posible que a más largo plazo se realice un compilador de Self como prueba del sistema. En términos de sistema orientado al aprendizaje, los sistemas más parecidos son el lenguaje de programación Blue, con su entorno, de mismo nombre (Kölling, 1999b), y BlueJ, sistema posterior, basado en Java pero de muy similares características (Kölling, 2003). La mayor diferencia entre Zero y este sistema, reside en la natural dinamicidad de Zero (basado en prototipos) en contra de la rigidez estática de Java y Blue (ambos basados en clases), como ya se ha comentado. Cada una tiene sus ventajas e inconvenientes, pero los autores defendemos que la dinamicidad puede presentar características muy positivas. Por otra parte, el sistema presentado aquí no posee ningún entorno integrado de programación que refuerce los aspectos educativos, al contrario que Blue y BlueJ. Su entorno es considerado por los autores de este artículo como ideal para el aprendizaje, y en el futuro se considerará la implementación de uno similar. Oviedo3 (Ortín Soler et al., 2000), es una máquina virtual parecida también a Zero, puesto que, a su vez, está inspirada parcialmente en Self. Sin embargo, Oviedo3 está basada en clases, como Smalltalk, lo cuál no cumple con los objetivos planteados por nosotros los autores. VII Congresso Iberoamericano de Informática Educativa 1353 4. Conclusiones Zero es una máquina virtual orientada a objetos pura, multiplataforma, basada en prototipos, y, por tanto, especialmente diseñada para soportar lenguajes dinámicos. El sistema presenta una manera de programar clara, con una sintaxis (incluso empleando un macro esamblador) simple, y soportando de manera sencilla los conceptos de programación orientada a objetos. Su modelo de orientación a objetos es también simple y muy sencillo de entender y aprender, ya que el número de elementos a tratar es muy pequeño2. No soporta tipos de forma natural (si bien sería posible crear un lenguaje de programación que sea un subconjunto de Java o C++ que sí lo haga, puesto que se engloba el modelo basado en clases), pero esto los autores lo defienden como una ventaja, y no como un inconveniente. Pese a estar en un momento de desarrollo bastante inicial, el sistema ya ha sido empleado con éxito en un curso de doctorado, y en una asignatura de la carrera de Ingeniería en Informática. Gracias al empleo de encuestas, se ha obtenido bastante información que esperamos poder procesar y publicar en breve. Sí está claro, al menos, por parte de los usuarios del sistema, que ven como punto de desarrollo futuro más importante la implementación de un lenguaje de alto nivel, lo cuál será la más alta prioridad de los autores. 5. Trabajo futuro El soporte de persistencia es uno de los puntos más importantes del proyecto, ya implementada parcialmente. De hecho, sólo es necesario proveer a Zero de un almacenamiento persistente (persistent store, Balch et al., 1989). Otros proyectos incluídos en el futuro inmediato de Zero son la creación de un lenguaje de alto nivel y de un entorno integrado, como ya se ha comentado. 6. Bibliografía Atkinson M.P., Morrison R. (1995). "Orthogonality Persistent Object System", VLDB Journal 4(3), 319-401, ISSN: 1066-8888 Atkinson, M., Jordan, M. (2000). A Review of the Rationale and Architectures of Pjama: A Durable, Flexible, Evolvable, and Scalable Orthogonaly Persistent Programming Platform. Sun Microsystems Technical Report SMLI TR-2000-90 Balch, P., Cockshott, W.P., Foulk, P.W. (1989). Layered Implementations of persistent object stores. Software Engineering Journal, 4(2), 123-131. 2De ahí su nombre, Zero, como cero, lo más pequeño y mínimo. VII Congresso Iberoamericano de Informática Educativa 1354 Black A. (2003). Post Javaism. Proceedings of the First Workshop on Object-Oriented Language Engineering for the Post-Java Era: Back to Dynamicity. European Conference on Object-Oriented Programming. García Perez-Schofield, B., Pérez Cota, M. (1998). Problemática del uso de ficheros en C++: un enfoque educativo. Revista de Enseñanza y Tecnología. 1(12), 10-20. García Perez-Schofield B., Cooper, T.B., Pérez Cota, M., García Roselló E. (2001). Extending Containers to Address the Main Problems of Persistent Object Oriented Operating Systems: Clustering, Memory Protection and Schema Evolution. Proceedings of the Fourth Workshop on Object-Orientation and Operating Systems. European Conference on ObjectOriented Programming. García Perez-Schofield, J., Cooper, T., Roselló, E., Pérez Cota, M. (2002a). Persistencia, evolución del esquema y rendimiento en los sistemas basados en contenedores. Tesis Doctoral. Departamento de Informática. Universidad de Vigo. García Perez-Schofield B., Cooper, T.B., Pérez Cota, M., García Roselló E. (2002b). First Impressions about Executing Real Applications in Barbados. Proceedings of the Fifth Workshop on Object-Orientation and Operating Systems. European Conference on ObjectOriented Programming Goldberg A., Robson D. (1989). Smalltalk-80: The Language. Addison Wesley. ISBN 0201-13688-0 Kölling, M., Quig, B., Patterson, A., Rosenberg, J. (2003). The BlueJ System and its Pedagogy. Journal of Computer Science Education, v13(4). Kölling, M. (1999a). The problem of teaching Object-Oriented Programming. Journal of Object Oriented Programming, v11(8). Kölling, M. (1999b). The Blue Language. Journal of Object Oriented Programming, v11(10). LaLonde, W. (2000). I can read C++ and Java, but I can't read Smalltalk. Journal of ObjectOriented Programming. February, 2000. Martínez A., Álvarez García F., Ortín Soler F., Cueva Lovelle J. (2000). An OODBMS developed over the Oviedo3 Operating System. Proceedings of the Simposio Español de Informática Distribuida (SEID'2000). Naughton P. (1996). The Java Handbook. McGraw-Hill Ungar, D., Smith, R. S. (1987). "Self: The Power of Simplicity". In Proceedings of VII Congresso Iberoamericano de Informática Educativa 1355 OOPSLA'87, Orlando, FL, pp. 277-241. Shapiro, M., Kloosterman, S., Riccardi, F. (1997). "PerDis. A Persistent Distributed Store for Persistent Applications", Proceedings of the 3rd Capernet Plenary Workshop. Stroustrup B. (1991). The C++ Programming Language. Addison-Wesley Taivalsaari A. (1992). Kevo: A prototype-based object-oriented language based on concatenation and module operartions. Technical Report DCS--197--1R, University of Victoria, Victoria, British Columbia, Canada. VII Congresso Iberoamericano de Informática Educativa 1356