Documentos de Académico
Documentos de Profesional
Documentos de Cultura
Resumen
1. Introducción
Una técnica ampliamente usada en ingeniería del software es modelar un sistema mediante combinación de vistas del
sistema que son semánticamente compatibles [CD94]. El primer beneficio de esta aproximación es un mejor manejo
de los sistemas complejos y detección de inconsistencias en etapas tempranas del análisis. Normalmente, el
modelado se compone de una vista estructural, otra dinámica y otra funcional.
El modelo estructural describe la relación entre las clases de componentes del sistema y la configuración de los
propios componentes del mismo. El modelo dinámico describe el ciclo de vida de los componentes del sistema y el
modelo funcional describe las transformaciones de datos, e invariantes del sistema.
Llamamos formalismos de especificación gráfico-textual a notaciones gráficas (ej. Object Modelling Technique
(OMT), notación de Booch, OOSE, Unified Modelling Language (UML), ...etc.) más una notación formal para
especificar restricciones que se integra como texto en la notación anterior (ej. subconjuntos de Z o VDM, OCL, etc).
Esto nos permite disponer de precisión a través del lenguaje de restricciones sin perder el impacto visual de la parte
gráfica. Por otra parte, las notaciones gráficas de análisis (y diseño) son más accesibles que las notaciones asentadas
en la teoría formal de conjuntos.
El formalismo gráfico-textual adoptado en este trabajo es la combinación de UML (Unified Modelling Language)
[UML971] para la parte gráfica y Object Constraint Language (OCL) [OCL97] como lenguaje formal para
especificar restricciones. No ha habido una razón especial para la elección de UML, si bien nos parece una notación
que fusiona las notaciones Booch, OMT y OOSE. Sin embargo, si lo ha habido para la elección de OCL ya que se
trata de un lenguaje de fácil lectura y escritura y puede ser ejecutable sin necesidad de una traducción compleja.
OCL se caracteriza por ser un lenguaje sin efectos laterales; por tanto, no altera el estado del sistema. Se trata de un
lenguaje tipado; esto significa que toda expresión OCL tiene un tipo. En una expresión OCL todos los tipos deben
ser conformes; por ejemplo no podemos comparar un entero con una cadena. Toda expresión OCL es
conceptualmente atómica. El estado de los objetos en el sistema no puede cambiar durante su evaluación. Usaremos
OCL para especificar invariantes sobre clases y tipos en el modelo de clases, describir pre y post condiciones de
operaciones y métodos, describir guardas y como lenguaje de navegación a través del modelo.
Los diagramas de UML son: diagramas de objetos que representan los objetos y sus relaciones, diagramas de
secuencia que representan las interacciones de los objetos en el tiempo, diagramas de estado-transición que
representan el comportamiento de una clase en términos de estado, diagramas de clases que representan la estructura
estática en términos de clase, diagramas de actividades que representan el comportamiento de una operación en
términos de acciones, los diagramas de casos de uso que representan las funciones del sistema desde el punto de
vista del usuario, diagramas de colaboración que son una representación espacial de objetos, enlaces e interacciones,
diagramas de componentes que representan los componentes físicos de una aplicación y diagramas de despliegue
que representan el despliegue de componentes sobre los dispositivos físicos. Un ejemplo de modelo de clases es el
que aparece en la figura 1. El significado de dicho modelo es el siguiente: Estamos en el contexto de la universidad
donde los profesores vienen representados por la clase Profesor, los alumnos por la clase Alumno, los departamentos
por la clase Departamento, las asignaturas por la clase Asignatura, las universidades por la clase Universidad y las
personas de nuestro contexto por la clase Persona. Consideremos la generalización Persona como disjunta (toda
persona o es profesor o es alumno de manera exclusiva) y es completa (toda persona de nuestro interés es profesor o
alumno de manera obligatoria). La asociación entre Profesor y Alumno significa que todo profesor es tutor de
proyectos para 0 o más alumnos (0..*) y que todo alumno puede tener o no a un profesor como tutor. El resto de las
asociaciones se interpreta de manera análoga: todo profesor está en un sólo departamento y el departamento está
asociado con al menos un profesor, la asociación reflexiva de Profesor a Profesor significa que un profesor es tutor
de 0 o más profesores a los que les dirige su doctorado, .... y así con el resto de las asociaciones. La asociación con
terminación en diamante consiste en una agregación; con ella expresamos que una universidad está formada por al
menos 1 departamento y que los departamentos están subordinados a las universidades.
Figura 1: Especificación UML / OCL
2. El lenguaje OCL
Cualquier expresión OCL está escrita en el contexto de una instancia de un tipo/clase definido en un modelo UML,
en el cual la expresión toma pleno significado. OCL define un conjunto de tipos con operaciones asociadas. Todas
las expresiones OCL ligadas a un modelo amplían ese conjunto inicial con todos los tipos/clases definidos en el
modelo. OCL permite además definir tipos enumerados en el ámbito de un modelo, que también pasarían a formar
parte del conjunto de tipos. Podemos clasificar los tipos predefinidos en básicos y de colección. Los primeros son
Integer (números enteros), Real (números reales), Boolean (valores lógicos), String (caracteres y cadenas, sin
distinción semántica), Enumeration (cuyas instancias son las enumeraciones definidas en un modelo), OclExpression
(cuyas instancias son todas las posibles expresiones que pueden escribirse en OCL), OclAny (supertipo para todos
los tipos del modelo) y OclType (cuyas instancias son los tipos del modelo y los predefinidos en OCL). OclType
permite, por tanto, acceder al meta-nivel del modelo. Los tipos de colección son Collection (supertipo abstracto para
los demás tipos de colección), Set (que representa el conjunto matemático), Bag (o multiconjunto, que es un conjunto
en el que se permite elementos duplicados) y Sequence (que puede verse como un multiconjunto en el que los
elementos están ordenados). Los tipos de colección son genéricos, y deben ser instanciados con el tipo de los
elementos.
Las expresiones del lenguaje incluyen llamadas a las operaciones de los tipos predefinidos, accesos a los atributos y
operaciones de los tipos/clases de usuario, y navegación a través de asociaciones. Dado un objeto es posible acceder
a sus atributos o invocar sus operaciones mediante el operador punto (.) o, en el caso de que el objeto sea una
colección, el operador flecha (->). El operador punto permite además navegar a través de las asociaciones, indicando
el nombre del extremo opuesto de la asociación. En el caso más general, la navegación dará lugar a un Set. Con OCL
podemos especificar invariantes de clases, que son expresiones lógicas que han de cumplirse durante toda la vida de
las instancias de dichas clases, y especificaciones pre/post de operaciones. Consideremos la invariante para la clase
Departamento en el modelo ejemplo:
Departamento
------------
profesores->exists (prof | prof = self.director)
La expresión es cierta cuando en el conjunto de instancias que resulta al navegar hacia el extremo profesores de la
clase Departamento, existe un profesor que es el mismo que el que se obtiene al navegar hacia el extremo director.
Figura 2: Metamodelo UML (Subpaquete Core – parcial)
3. El metamodelo UML
El metamodelo UML [UML972] está organizado en tres paquetes: Foundation (que contiene los constructores de la
parte estructural de un modelo), Behavioral Elements (que encapsula los constructores de las partes funcional y
dinámica) y Model Management (que contiene constructores para la organización lógica y física de los elementos de
un modelo). La visión funcional la tenemos cubierta con el lenguaje de especificación OCL, y la visión dinámica
queda para un trabajo futuro, por lo que prescindiremos de Behavioral Elements. Foundation está estructurado en
cuatro subpaquetes: Core (donde se encuentran los constructores básicos), Auxiliary Elements (constructores que
complementan a los anteriores), Extension Mechanisms (constructores para extender UML) y Data Types (tipos de
datos necesarios para definir UML). La parte fundamental del paquete Core se presenta en la figura 2. El criterio
para interpretar el metamodelo consiste en recorrer el árbol jerárquico, partiendo de las hojas (que siempre son
metaclases no abstractas y, por tanto, sus instancias son elementos en un modelo de clases) y subiendo hacia la raíz.
En cada nivel hay que considerar los metatributos y navegar las metaasociaciones, completando de esta manera la
información sobre las metaclases de partida. Por ejemplo, Interfaz, Clase y TipoDeDato (cuyas instancias son las
interfaces, clases y tipos de datos en cualquier modelo UML) son subclases directas de Clasificador, y por ello son
elementos generalizables (metaclase ElementoGeneralizable), sirven de espacio de nombres para otros elementos
(metaclase EspacioDeNombres), tienen un nombre que las identifica (posiblemente dentro de un espacioDeNombres,
metaclase ElementoDeModelo), pueden tener restricciones (metarole restriccion de ElementoDeModelo), pueden
tener dependencias con otros elementos (no aparece en la figura), pueden poseer atributos, operaciones y métodos
(agregación exclusiva de Clasificador a Caracteristica, de la que cuelgan Atributo, Operacion y Metodo) y sirven de
extremos para las posibles asociaciones (no se muestra en el diagrama).
4. Mecanismo de traducción
Partimos de una representación del modelo en formato de texto, los ficheros con extensión mdl de la herramienta
Rational Rose. La traducción consta de cinco fases en cascada.
Departamento
------------
profesores->exists (prof | prof = self.director)
El código se genera como una función pública miembro de la clase java Departamento de la siguiente forma:
1) Resolución de la navegación a través del extremo profesores.
2) Llamada a la función miembro auxiliar que simulará la aparición del filtro exists (función iterate4).
3) Composición final del resultado.
Además:
4) Generar código para el filtro en la función miembro privada iterate4. Para ello importamos el esquema
genérico de implementación del exists y generamos código para la navegación hacia el extremo
director y para la comparación, lo cual es inmediato.
El código final quedaría:
public boolean invariante ()
{
boolean result; //resultado de la invariante
boolean x1;
Set x2;
x2= profesores; //Paso 1
boolean x9;
x9= iterate4(x2); //Paso 2
x1= x9;
result= x1; //Paso 3
return result;
}
El desarrollo formal de programas desde especificaciones es un tema ampliamente tratado a nivel de programación a
pequeña escala (por ejemplo, [Dro89], [Jac93], [Par90]). Éstos hacen hincapié principalmente en mostrar sólo la
existencia de leyes rigurosas en la construcción de programas pero no se centran en la viabilidad de las mismas a la
hora de su posible aplicación real y a una escala mayor (posiblemente en un entorno industrial). La orientación a
objetos también desplaza la atención de la síntesis clásica de programas. Hasta ahora, la mayor parte de los trabajos
de síntesis se centraban en la programación a pequeña escala y sin considerar los conceptos de orientación a objetos.
La necesidad de probar la equivalencia semántica entre especificación y programa nos lleva a adoptar algún tipo de
demostrador (por ejemplo, el proceso de obligación de prueba en VDM, paradigma de prueba como programa
[ConBat85], etc.) asociado al proceso de síntesis sin el cual no podríamos asegurar la validez de la transformación.
La ampliación del lenguaje de especificación con conceptos orientados a objetos nos lleva a un proceso de síntesis
más complejo (subsumiendo la situación anterior). La efectividad en la síntesis de código y su aplicación a gran
escala nos obliga a replantearnos las exigencias de pruebas necesarias en toda transformación y abordar el problema
no sólo desde la perspectiva clásica de los demostradores de teoremas generalistas, sino además, valiéndonos de la
experiencia en problemas bien conocidos y recurrentes y adoptando las soluciones a los mismos como punto de
partida que nos evitará, en parte, el "cuello de botella" que representa la aproximación clásica. El estudio de los
problemas y una metodología de especificación de los mismos nos ayudará a condicionar mejor el proceso de síntesis
evitándonos un gran trabajo de demostración.
7. Referencias
[CD94] S. Cook, J. Daniels. Designing Object Systems. Object-Oriented Modelling with Syntropy. Prentice Hall
1994.
[ConBat85] R.L. Constable and J.L. Bates. Programs as Proofs. Dept. of Computer Science, Cornell University,
November 1992. ACM Transactions on Programming Languages and Systems, Vol 7, Nº 1,1985
[Dro89] G. Dromey. Program Derivation. Addison-Wesley, 1989.
[Jac93] J. M. Jacquet. Constructing Logic Programs. J. Wiley and Sons, 1993.
[Ja97] J. Jaworski. Java: Guía de desarrollo. Prentice Hall 1997.
[OCL97] Rational Software, Microsoft y otros. Object Constraint Language Specification. Versión 1.1. Septiembre
1997.
[Par90] H.A. Partsch. Specification and Transformation of Programs. Springer Verlag, 1990.
[UML971] Rational Software, Microsoft y otros. Notation Guide. Versión 1.1. Septiembre 1997.
[UML972] Rational Software, Microsoft y otros. UML Semantics. Versi¢n 1.1. Septiembre 1997.