Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
TESIS DOCTORAL
Directores:
Me siento en deuda con los autores que cito en la bibliografía, auténticos gigantes sobre
cuyos hombros he desarrollado esta tesis. En especial, con Eugenio Arellano.
Agradezco a José Antonio Cerrada y a José Félix Estívariz, directores de esta tesis,
sus comentarios y el entusiasmo que han puesto en este trabajo.
A mis compañeros, Juan José Escribano, S. Rubén Gómez, José Luis Gayo, Carlos
Vicente Álvarez, Juan Antonio Mascarell, Ismael Abad y David Fernández, les agradezco su
apoyo técnico y moral.
A Mari Carmen Cuéllar, María Gil, Juan Antonio Heradio, Israel Heradio, Jesús
Muñoz y Gerardo Ramos, les agradezco su “soporte afectivo”.
Muchas gracias.
Pasaba largas horas en su cuarto, haciendo cálculos sobre las
posibilidades estratégicas de su arma novedosa, hasta que logró
componer un manual de una asombrosa claridad didáctica y un
poder de convicción irresistible.
- Los requisitos fijos de una familia de productos suelen ser más estables que los
requisitos variables. EDD separa la implementación de los requisitos fijos (el
ejemplar) de la implementación de los requisitos variables (los módulos que
flexibilizan el ejemplar).
10
Abstract
Many authors consider that product family development, instead of one-off development,
is a decisive step towards the systematic software reuse and economy of scope. This thesis
is aligned with this movement and proposes a new process to build product families,
named EDD (Exemplar Driven Development), which takes advantage of the similarity between
family products to make them by analogy.
The first EDD activity is to build a specific product of a family. Next, this exemplar
is flexibilized to satisfy the remaining products requirements. That is, an analogy relation is
defined in a formal way to derive products automatically from the exemplar. Finally, the
family products are obtained parametrizing the exemplar flexibilization.
- Family fixed requirements are usually more stable than variable requirements.
EDD separates the fixed requirements implementation (located in the
exemplar) from the variable requirements implementation (located in the
modules which make the exemplar flexible).
This thesis explores different ways to flexibilize an exemplar by applying the most
common techniques of code generalization (inheritance, genericity, code templates…).
Unfortunately, the thesis demonstrates that these techniques suffer limitations which
hinder flexibilizations with important qualities like modularity, non-invasiveness,
applicability to any software product… In order to avoid these limitations, the thesis
11
proposes a new language called ETL (Exemplar Transformation Language) which is
implemented in Ruby.
With the purpose of illustrating the power and versatility of EDD and ETL, the
thesis includes examples of the development of programs written in Java and C++; stored
procedures written in TRANSACT SQL; tests suites written in Java and Modula-2; and
documentation written in HTML and Javadoc.
12
Índice general
1. Introducción
13
3.2.1.1. Definición del dominio ..........................................................................77
3.2.1.2. Identificación y clasificación de los requisitos de la familia de
productos ...............................................................................................................78
3.2.1.3. Modelado del dominio ...........................................................................79
3.2.1.4. Análisis de la rentabilidad de la flexibilización del ejemplar .............79
3.2.1.5. Ajuste del modelo del domino..............................................................79
3.2.1.6. Selección de los requisitos de un ejemplar ..........................................79
3.2.2. Definición de una interfaz para la especificación abstracta de los productos 80
3.2.2.1. Especificación de un DSL .....................................................................80
3.2.2.1.1. Sintaxis .....................................................................................80
3.2.2.1.2. Semántica.................................................................................83
3.2.2.2. Cualidades deseables de un DSL ..........................................................83
3.2.3. Implementación de la flexibilización del ejemplar..............................................84
3.2.4. Flexibilización iterativa ...........................................................................................87
3.3. Obtención de productos a partir de la flexibilización del ejemplar ................................. 88
6. Ejemplos de aplicación
Bibliografía……………………..…………………………………………………….215
16
Lista de símbolos
17
TUG Tree Unified with Grammar
UML Unified Modeling Language
W3C World Wide Web Consortium
XMI XML Model Interchange
XML eXtensible Markup Language
XSLT Extensible Stylesheet Language Transformations
18
Índice de figuras
20
Figura 5.10. Arquitectura de un intérprete de diccionarios. ...................................................114
Figura 5.11. Codificación de un intérprete de diccionarios. ...................................................114
Figura 5.12. Arquitectura de un compilador de diccionarios. Generación desde cero,
escribiendo sobre un flujo de texto............................................................................................115
Figura 5.13. Codificación de un compilador de diccionarios. Generación desde cero,
escribiendo sobre un flujo de texto............................................................................................116
Figura 5.14. Diccionario de inglés generado por la infraestructura de la figura 5.13 a partir
de la especificación de la figura 5.8. ...........................................................................................116
Figura 5.15. Diccionario deModula-2 generado por la infraestructura de la figura 5.13 a
partir de la especificación de la figura 5.9. ................................................................................116
Figura 5.16. Arquitectura de un compilador de diccionarios. Generación con ETL por
analogía...........................................................................................................................................117
Figura 5.17. Diseño detallado de un compilador de diccionarios. Generación con ETL por
analogía...........................................................................................................................................117
Figura 5.18. Codificación de un compilador de diccionarios. Generación con ETL por
analogía...........................................................................................................................................118
Figura 5.19. Gramática, equivalente a la de la figura 5.7, de un DSL interno a Ruby ........118
Figura 5.20. Especificación, según la gramática de la figura 5.18, de un diccionario de
inglés...............................................................................................................................................118
Figura 5.21. Ejecución, paso a paso, del análisis interno realizado por el intérprete de Ruby
de la especificación de la figura 5.20..........................................................................................119
Figura 5.22. Modelo FODA del dominio “listas para C++”.................................................122
Figura 5.23. Correspondencias entre los nodos del diagrama de la figura 5.22, las
responsabilidades que debe asumir la línea de productos y las categorías de componentes
que deben desarrollarse................................................................................................................122
Figura 5.24. Dependencias de uso entre las categorías de componentes. ............................123
Figura 5.25. Organización de las categorías de componentes en una arquitectura por capas.123
Figura 5.26. Gramática GenVoca que especifica las posibles combinaciones entre
componentes. ................................................................................................................................124
Figura 5.27. Diseño detallado, obtenido con la metodología propuesta en [CE00], de la
línea de productos “listas para C++”. .......................................................................................125
Figura 5.28. Codificación de la línea de productos “listas para C++” obtenida con la
metodología propuesta en [CE00]. ............................................................................................127
Figura 5.29. Ejemplo de almacén de configuración. ...............................................................128
Figura 5.30. Combinación de las clases de la figura 5.28, que satisface la especificación de la
figura 5.29. .....................................................................................................................................128
Figura 5.31. Equivalencia entre notaciones FODA.................................................................129
Figura 5.32. Requisitos de la familia “listas para C++” seleccionados para el desarrollo de
un ejemplar. ...................................................................................................................................129
21
Figura 5.33. Ejemplar de la familia “listas para C++”............................................................ 130
Figura 5.34. Gramática de un DSL para especificar listas. ..................................................... 131
Figura 5.35. Especificación de una lista según la gramática de la figura 5.34...................... 131
Figura 5.36. Gramática, equivalente a la de la figura 5.34, de un DSL interno a Ruby...... 131
Figura 5.37. Especificación de una lista según la gramática de la figura 5.36...................... 131
Figura 5.38. Diseño de la flexibilización ETL del ejemplar. .................................................. 132
Figura 5.39. Codificación de la flexibilización ETL del ejemplar.......................................... 133
Figura 5.40. Análisis de las especificaciones DSL y ejecución coordinada de los
generadores ETL......................................................................................................................... 134
Figura 5.41. Orden de aplicación de los generadores ETL.................................................... 134
Figura 5.42. Flexibilización del ejemplar con sentencias de selección................................. 136
Figura 5.43. Registro para especificar listas según la flexibilización de la figura 5.42. ....... 136
Figura 5.44. Especificación de una lista, lograda parametrizando el registro de la figura
5.43. ................................................................................................................................................ 136
Figura 5.45 Generalización del ejemplar con subprogramas. ................................................ 138
Figura 5.46. Diseño de una flexibilización del ejemplar basada en la composición y la
herencia de clases.......................................................................................................................... 139
Figura 5.47. Codificación de una flexibilización del ejemplar basada en la composición y la
herencia de clases.......................................................................................................................... 142
Figura 5.48. Ejemplo de especificación de una lista para la flexibilización de la figura 5.42.142
Figura 5.49. Flexibilización del ejemplar basada en la composición y la herencia de clases.144
Figura 5.50. Ejemplo de especificación de una lista para la flexibilización de la figura 5.49.145
Figura 5.51. Diseño de una flexibilización del ejemplar basada en herencia múltiple........ 145
Figura 5.52. Modificaciones necesarias para la flexibilización del ejemplar......................... 147
Figura 5.53. Diseño de una flexibilización del ejemplar basada en herencia simple. ......... 148
Figura 5.54. Extracto de la codificación de una generalización del ejemplar basada en
herencia simple. ............................................................................................................................ 150
Figura 5.55. Diseño de una flexibilización del ejemplar basada en herencia parametrizada.150
Figura 5.56. Codificación de una generalización del ejemplar basada en herencia
parametrizada................................................................................................................................ 153
Figura 5.57. Diseño, según la notación propuesta en [Zha05], de una flexibilización del
ejemplar basada en orientación a aspectos. .............................................................................. 155
Figura 5.58. Flexibilización del ejemplar con plantillas ERB................................................. 157
Figura 5.59. Análisis de especificaciones DSL y generación de listas con plantillas ERB. 157
Figura 6.1. Model del dominio “documentación de la creación de tablas en SQL”. ......... 163
Figura 6.2. Representación en un navegador del ejemplar de SQLDoc............................... 164
Figura 6.3. Ejemplar de SQLDoc (index.html)........................................................................ 164
Figura 6.4.Ejemplar de SQLDoc (Tables.html)....................................................................... 164
Figura 6.5. Ejemplar de SQLDoc (Book.html)........................................................................ 165
22
Figura 6.6. Sintaxis abstracta de un DSL para documentar la creación de tablas en SQL.165
Figura 6.7. Sintaxis concreta de un DSL para documentar la creación de tablas en SQL. 166
Figura 6.8. Ejemplo de código SQL documentado (banco.sql).............................................167
Figura 6.9. Generación obtenida por SQLDoc a partir de la figura 6.8 (Tables.html).......167
Figura 6.10. Generación obtenida por SQLDoc a partir de la figura 6.8 (Cuenta.html). ..167
Figura 6.11. Arquitectura de SQLDoc.......................................................................................168
Figura 6.12. Diseño del generador SQLDoc. ...........................................................................169
Figura 6.13. Código de SQLDoc (SQLDoc.rb). ......................................................................170
Figura 6.14. Ejemplo de fichero de datos de personas (“data.csv”). ....................................171
Figura 6.15. Lector de datos de personas (“PeopleReader.java”)..........................................173
Figura 6.16. Modelo del dominio “lectores de ficheros CSV”...............................................174
Figura 6.17. Documentación, generada con la herramienta javadoc, del lector de personas.174
Figura 6.18. Juego de pruebas para el lector de personas (“PeopleTest.java”)....................175
Figura 6.19. Gramática EBNF del DSL con el que se especificarán los lectores de ficheros
CSV.................................................................................................................................................176
Figura 6.20. Especificación XML de un lector de libros. .......................................................176
Figura 6.21. Ejemplo de fichero de datos para el lector especificado en la figura 6.20......176
Figura 6.22. Diseño del compilador de “lectores de ficheros CSV”.....................................177
Figura 6.23. Analizador de las especificaciones de programas lectores de datos. ...............177
Figura 6.24. Ejemplo de generación del programa objeto especificado en la figura 6.20..178
Figura 6.25. Generador ReaderGen...........................................................................................179
Figura 6.26. Generador TestGen. ..............................................................................................180
Figura 6.27. Módulo auxiliar CSV_Utilities. .............................................................................180
Figura 6.28. Suma de los generadores ReaderGen y TestGen...............................................181
Figura 6.29. Copia de datos entre tablas originales y temporales. .........................................181
Figura 6.30. Tabla de seguimiento de la copia de datos..........................................................182
Figura 6.31. Procedimientos almacenados para la copia de los datos contenidos en la tabla
TbCompProp. ...............................................................................................................................182
Figura 6.32. Procedimiento almacenado sp_AprovTbCompProp........................................187
Figura 6.33. Procedimiento almacenado sp_ActuTbDiaCtlPrc.............................................188
Figura 6.34. Procedimiento almacenado sp_ActuTbCompProp. .........................................190
Figura 6.35. Procedimiento almacenado sp_ExisTbCompProp. ..........................................191
Figura 6.36. Modelo del dominio “copiadores de datos entre tablas”..................................192
Figura 6.37. Diseño del compilador de “copiadores de datos entre tablas”. .......................193
Figura 6.38. “Análisis” de la especificación de los procedimientos almacenados para las
tablas TbAtrTb y TbTb. Ejecución del generador AprovTbGen. ........................................194
Figura 6.39. Ejemplo de generación de los procedimientos almacenados que copian los
datos de la tabla TbAtrTb. ..........................................................................................................194
Figura 6.40. Generador AprovTbGen.......................................................................................197
23
Figura 6.41.Módulo Util. ............................................................................................................. 198
Figura 6.42. Módulo Util enriquecido con las pruebas Test1 y Test2. ................................. 199
Figura 6.43. Resultado de ejecutar con m2unit la figura 6.42. ............................................... 200
Figura 6.44. Ejemplo de actuación de los generadores de m2unit........................................ 200
Figura 6.45. Módulo principal Test1 generado por m2unit. .................................................. 202
Figura 6.46. Módulo principal Test2 generado por m2unit. .................................................. 203
Figura 6.47. Código de m2unit................................................................................................... 207
Figura 6.48. Programa Java enriquecido con la notación aritmética para la clase
BigDecimal. ................................................................................................................................... 207
Figura 6.49. Programa Java convencional equivalente a la figura 6.48................................. 208
Figura 6.50. Preprocesador BigDecEqGen escrito en ETL. ................................................. 209
Figura 6.51. Compilador BigDecEqParser escrito en Racc. .................................................. 210
24
Introducción 25
1
Introducción
Nothing is really unprecedented. Faced with a new situation, people liken it to familiar ones
and shape their response on the basis of the perceived similarities.
− Reducción de costes.
− Desarrollo acelerado.
Muchos autores [CE00, Nei80, Par76] consideran que este fracaso se debe a que la
mayoría de los procesos de desarrollo de software, ya sean formales o ágiles, persiguen la
construcción de productos aislados (one-off development). Al no disponerse de contextos
suficientemente amplios como para detectar con precisión qué elementos son reutilizables
y cuáles son las situaciones donde puede sacarse más partido a la reutilización, se
desemboca en una reutilización oportunista del software. Para que la reutilización del
software fuera sistemática, los procesos de desarrollo deberían abordar la construcción
colectiva de familias de productos1 relacionados por un dominio.
1 D. Parnas [Par76] fue quien acuñó el término “familia de programas” para designar a un conjunto de
programas lo suficientemente similares como para que sea más rentable resolverlos colectivamente que por
separado: “We consider a set of programs to constitute a family, whenever it is worth-while to study programs from the set by
first studying the common properties of the set and then determining the special properties of the individual family members.”
Introducción 27
La economía de escala ocurre sobre todo durante la fase de producción. Como señala
P. Weger [Weg78], la naturaleza esencialmente lógica del software hace que los costes se
concentren en la etapa de desarrollo (el coste de producir las copias de un sistema
informático es despreciable comparado con el coste de desarrollo del sistema) y, por tanto,
sea la economía de alcance el principio más aplicable en la fabricación de software.
Actualmente, se considera que los marcos de trabajo son abstracciones de caja gris
(gray box abstractions) [GS04] y como consecuencia,
2 Por abstracción suele entenderse el acto de quedarse con lo esencial prescindiendo de los detalles que, desde
algún punto de vista, son irrelevantes. La abstracción:
Introducción 29
La figura 1.2 resume el proceso EDD, que se descompone en tres actividades básicas:
Fuera del contexto de un dominio, la especificación formal de un producto exigiría definir todos sus
requisitos. Sin embargo, dentro de un dominio, la especificación para cada producto de los requisitos
comunes y con un valor fijo es superflua y puede omitirse. Este ahorro posibilita que un DSL pueda ser más
abstracto que cualquier lenguaje de propósito general (GPL, General Purpose Language).
3 El diccionario de la Real Academia de la Lengua Española incluye las siguientes definiciones para
“analogía”:
“Creación de nuevas formas lingüísticas, o modificación de las existentes, a semejanza de otras; por ejemplo, los
pretéritos tuve, estuve, anduve se formaron por analogía con hube.”
30 Capítulo 1
que satisface los requisitos comunes de todos los productos. Después, se van
incorporando capas de flexibilización que implementan los requisitos variables4.
- Los requisitos comunes de una familia de productos suelen ser más estables que
los requisitos variables. EDD separa la implementación de los requisitos
comunes (el ejemplar) de la implementación de los requisitos variables (los
módulos que flexibilizan el ejemplar).
4En la sección 3.2.4 se explicará la integración de EDD con el modelo de ciclo de vida en espiral propuesto
por B. Boehm [Boe88].
Introducción 31
En la sección 2.1 se verá que el código es el producto del ciclo de vida con mayor
tasa de reutilización. Por esta razón, las técnicas que comúnmente se emplean para
generalizar código y posibilitar su reutilización parecen buenas candidatas para la
flexibilización de un ejemplar. En el capítulo 5 se examinará la viabilidad de muchas de
5 La no invasividad entre módulos (invasiveness) es una de las metas de la orientación a aspectos [Lad03a].
32 Capítulo 1
estas técnicas para la flexibilización y se comprobará que padecen serias limitaciones. Con
el fin de superar dichas limitaciones se propone el lenguaje ETL (Exemplar Transformation
Language), original de esta tesis.
Esta tesis incluye una implementación de ETL en Ruby [Rub07] que permite la
flexibilización de ejemplares escritos en cualquier lenguaje. Concretamente, los capítulos 4,
5 y 6 mostrarán ejemplos de flexibilización de programas escritos en Java, C++ y
TRANSACT SQL; de juegos de prueba escritos en Java y Modula-2; y de documentación
escrita en HTML y Javadoc.
Flexibilización invasiva
Flexibilización no invasiva
6Un módulo ETL puede actuar sobre distintas partes de un ejemplar. Por ejemplo, si el ejemplar fuese un
programa orientado a objetos, un módulo ETL podría actuar sobre distintas clases.
Introducción 33
- El capítulo 7 resume las conclusiones de esta tesis y los trabajos futuros que pueden
desarrollarse a raíz de ella.
2
Estado del arte
Though some pundits have suggested that there has been more reuse of the word “reuse” than
practice of it, it’s undoubtedly the case that a major area of progress in our industry has
involved enabling reuse.
La sección 2.1 del presente capítulo resume importantes logros, problemas y áreas
de investigación en la reutilización del análisis, el diseño, la codificación y las pruebas de
software.
Para paliar estas carencias, existen algunas propuestas de uso de distintos grados de
formalización:
7 No así su búsqueda y selección, para las que existe un escaso soporte automático debido a que normalmente
la semántica de los componentes de código se especifica en lenguaje natural.
Estado del arte 37
y dos contenedores alternativos para almacenar sus elementos (Vector y Deque). Los
parámetros se expresan con trazo discontinuo.
Figura 2.1. Pila genérica y dos contenedores alternativos para almacenar sus elementos.
2. No permiten expresar los métodos que obligatoriamente deberá tener una clase
utilizada como parámetro real.
3. Un parámetro se puede usar con diversos fines, por ejemplo, para expresar el
tipo de una superclase (herencia parametrizada), el tipo de un objeto asociado, el
tipo del argumento de un método… En UML es muy difícil, si no imposible,
representar explícitamente el propósito de un parámetro.
− Los lenguajes de especificación formal, como Z [Dil94] ó TUG (Tree Unified with
Grammar) [Chi03], están libres de ambigüedad y permiten la definición de
especificaciones genéricas que pueden particularizarse y combinarse. Sin embargo,
como indica M. Chechik [Che98], el uso de los métodos formales está poco extendido
porque suelen considerarse costosos y difíciles de usar8.
8“There is a strong resistance against adopting formal methods in practice, especially outside the domain of safety-critical systems.
The primary reason for this resistance is the perception of software developers regarding the applicability of formal methods – these
38 Capítulo 2
Para que los requisitos puedan reutilizarse independientemente, interesa que sean
autocontenidos. Lamentablemente, esta situación no es frecuente. Si, por ejemplo, tres
requisitos X, Y, Z hacen referencia a un cuarto requisito R, pueden tomarse varias
decisiones:
methods are considered to be hard (require a level of mathematical sophistication beyond that possessed by many software
developers), expensive, and not relevant for ‘real’ software systems”.
Estado del arte 39
9 “A pattern for software architecture describes a particular recurring design problem that arises in specific design contexts, and
presents a well-proven generic scheme for its solution. The solution scheme is specified by describing its constituent components,
their reponsibilities and relationships, and the ways in which they collaborate”. [BMRSS96]
10Posteriormente, se publicaron dos secuelas de este libro conocidas como POSA2 (Schmidt, D.; Stal, M.;
Rohnert, H.; Buschmann, F. Pattern-Oriented Software Architecture, Volume 2, Patterns for Concurrent and Networked
Objects. John Wiley & Sons, 2000) y POSA3 (Kircher, M.; Jain, P. Pattern-Oriented Software Architecture, Volume 3,
Patterns for Resource. John Wiley & Sons, 2004).
40 Capítulo 2
Especificación
La ambigüedad con que están expresados los patrones puede provocar su mal uso y limita
su gestión automática mediante herramientas informáticas.
Ocultación
Tal como se especifican en [GHJV94], los patrones son unidades de reutilización de caja
blanca [GS04]: para usar el patrón no basta con conocer su propósito y “parámetros
variables”, sino que es imprescindible sumergirse en sus interioridades y adaptarlo
manualmente a cada situación particular.
Figura 2.7. Modelo generativo ofrecido por CO2P2S y Meta-CO2P2S para la encapsulación de patrones de diseño.
11 Una estrategia común para elevar la abstracción de un lenguaje es delegar decisiones en el compilador o
intérprete. El número de decisiones que puede delegar un lenguaje de propósito general (GPL, General Purpose
Language) sin mermar su generalidad es muy reducido, lo que limita las posibilidades de abstracción. Un DSL
se adecua a un ámbito particular donde pueden darse por sentado ciertas tareas y conceptos que se manejan
con frecuencia, lo que contribuye a incrementar significativamente su nivel de abstracción y cercanía al
usuario.
44 Capítulo 2
Acoplamiento
“Es posible hacer edificios enlazando patrones, de un modo poco preciso. Un edificio construido así
es una mezcolanza de patrones. No es denso. No es profundo. También es posible juntar patrones de modo
que muchos patrones se solapen en un mismo espacio físico: el edificio es muy denso; tiene muchos
significados representados en un espacio reducido; y a través de esa densidad, se hace profundo.”
Eficiencia
Muchos de los patrones recogidos en [GHJV94] ofrecen la adaptabilidad de los diseños por
medio de enlace dinámico (dynamic binding), lo que merma la eficiencia en tiempo de
ejecución de sistemas que implementen un número considerable de estos patrones.
12 “A constructional design pattern is an object-oriented design pattern, it has well defined interfaces, and it provides a solution
that is an abstraction of a common design structure in the form of a class model. The name constructional is given to these
patterns because they are used in constructing the structure of the application design. Constructional design patterns are design
components that can be glued together at a high design level. This composition defines the application overall solution structure.
Constructional design patterns do not necessarily map to structural design patterns defined by Gamma et al. Gamma's behavioral
patterns, such as State or Observer patterns, can also be considered constructional because they provide class diagrams as a
structure for composition. Patterns that do not provide a solution structure as class models are not considered constructional; e.g.
the Pipes-and-Filters or Layered-architecture patterns”.
46 Capítulo 2
Algunos autores [Alex01, CE00] han propuesto diseños alternativos para estos patrones
sustituyendo el enlace dinámico (en tiempo de ejecución) por el uso de genericidad (en
tiempo de compilación).
13“A framework defines an aplication skeleton that can be refined and customised by developers to create a range of different
applications” [KRW02].
14 “A framework is a set of cooperating classes that make up a reusable design for a specific class of software” [Deu89].
Estado del arte 47
marco que, como señalan M. Morisio et al. [MRS02], suele obtenerse generalizando
software legado15.
Una aplicación compleja puede reutilizar varios marcos, lo que abre la posibilidad de
solapamientos y colisiones [MB97]. Por ejemplo, en la reutilización basada en marcos de
trabajo, respecto a la basada en librerías de componentes, se da una inversión del flujo de
control. El código nuevo de una aplicación no invoca a elementos prefabricados, sino que
ocurre al revés, el marco de trabajo es quien invoca al nuevo código17. La figura 2.9
representa una aplicación que reutiliza los marcos A, B y C. El posible acceso simultáneo
de los marcos al código de la aplicación que los particulariza plantea los problemas clásicos
de la concurrencia (estados inconsistentes, interbloqueos…).
15 “Successful frameworks have evolved from reengineering long-lived legacy applications, abstracting from them the knowledge of
principal software designers”
16 Según [GS04], los marcos de trabajo son abstracciones de caja gris (gray box abstractions)
17 Popularmente, esto se conoce como “el principio de Hollywood” (Hollywood principle: “Don’t call us – we call
you”)
48 Capítulo 2
Figura 2.9. Inversión del flujo de control y colisiones en los marcos de trabajo.
18El término “subprograma” engloba a las “funciones” y a los “procedimientos”. Generalmente, los lenguajes
orientados a objetos utilizan el término “método” en lugar de subprograma.
Estado del arte 49
19Generalmente, se acepta que la memoria humana se divide en tres sistemas: memoria sensorial, memoria
operativa o a corto plazo y memoria a largo plazo. La memoria operativa es el sistema donde el usuario
maneja la información a partir de la cual está interactuando con el medio ambiente. Según G. A. Miller
[Mil56], esta información está limitada aproximadamente a 7 ± 2 conceptos durante 20 segundos si no se
repasa.
50 Capítulo 2
AspectJ
20 design bloat
21 Principio acuñado popularmente como YAGNI: “You Aren’t Gonna Need It”.
52 Capítulo 2
1. Join points. Son los lugares de un programa donde puede engancharse un aspecto.
AspectJ ofrece un abanico muy amplio de tipos de join points: llamada a métodos, acceso
a atributos, inicialización de clases y objetos… Algunos join points se consideran
conflictivos y no están disponibles para el programador22.
Líneas de investigación
22 Los join points disponibles para el programador se denominan exposed join points.
Estado del arte 53
23 “Generative Programming (GP) is a software engineering paradigm based on modeling software system families such that,
given a particular requirements specification, a highly customized and optimized intermediate or end-product can be automatically
manufactured on demand from elementary, reusable implementation components by means of configuration knowledge.” [CE00]
24El resultado de un modelo generativo no tiene porque ser un programa completo. Puede ser un programa
parcial, un componente de otros programas [JS00, WLKL04] o cualquier artefacto software: documentación,
juegos de pruebas… En esta tesis, se seguirá la tendencia más difundida de utilizar el término “familia de
programas” para designar de forma general familias de todo tipo de artefactos software.
54 Capítulo 2
Los autores recomiendan aplicar este proceso de forma incremental e iterativa. Por
ejemplo, el conocimiento de configuración se debería automatizar gradualmente: antes de
utilizar generadores, los componentes del espacio de la solución se parametrizarían y
ensamblarían manualmente.
El análisis del dominio de un modelo generativo es una actividad crítica que debe
evitar:
No disponible
No disponible
La primera compañía que adoptó el término factoría de software para identificar una
forma de organización del trabajo fue Hitachi en 1969 con su Hitachi Software Works.
Posteriormente, se sumaron otras compañías como la norteamericana System Development
Corporation en 1975 y las japonesas NEC, Toshiba y Fujitsu durante 1976 y 1977.
25Según K. Czarnecki [CE00] y P. Clements [CN99], los términos línea de productos y familia de programas
hacen referencia a dos formas diferentes de percibir un dominio. Desde un punto de vista orientado al
problema, una línea de productos se define como un conjunto de programas que satisfacen alguna necesidad
58 Capítulo 2
► Tablas donde las columnas indican conceptos y las filas niveles de abstracción.
Por ejemplo, la figura 2.18 es el esquema propuesto por J. Zachman [Zac04]
para aplicaciones empresariales. Cada celda es un punto de vista que aglutina
productos, elementos constituyentes y procesos.
► Grafos donde los nodos representan puntos de vista y los arcos relaciones entre
puntos de vista. La figura 2.17 es un ejemplo de esquema incluido en [GS04].
Las relaciones sirven para mantener la coherencia entre los puntos de vista y
capturar el conocimiento sobre como obtener los artefactos de un punto de
vista a partir de los artefactos incluidos en otro punto de vista. Si las
descripciones de los artefactos son formales, es posible automatizar la
conversión, que puede ser completa o parcial. En ocasiones, la conversión entre
puntos de vista no es única y su optimización depende del contexto. Por
ejemplo, existen numerosas maneras de modelar la herencia para el
almacenamiento de objetos en una base de datos relacional (utilizar una tabla
para guardar una jerarquía completa; por cada clase hija, usar una tabla que
incluya los atributos de la clase padre…) [Amb00]. Cada alternativa tiene sus
del mercado. Desde una perspectiva orientada a la solución, una familia de programas es un conjunto de
programas software muy similares entre sí. En esta tesis, se utilizarán indistintamente los dos términos.
Estado del arte 59
Figura 2.17. Representación mediante un grafo del esquema de una factoría de software propuesto por J. Greenfield
[GS04]
26Como indica el Free Online Dictionary on Computing (http://foldoc.org/), en ocasiones el acrónimo IDE
también se traduce por Interactive Development Environment.
60
Figura 2.18. Representación tabular del esquema de una factoría de software propuesto por J. Zachman [Zac04]
Capítulo 2
Estado del arte 61
[BRJ99, página 5]
Un modelo es un conjunto de elementos que describe una realidad física, abstracta o hipotética. Un
buen modelo sirve como medio de comunicación; es más barato de construir que un sistema real; y sirve de
soporte para la implementación. El grado de detalle de un modelo puede variar desde un boceto gráfico hasta
un modelo totalmente ejecutable.
− Los modelos suelen ser poco comprensibles para el cliente. En la comunicación cliente-
analista es preferible emplear prototipos.
− Los lenguajes de modelado tipo UML son ambiguos, lo que impide su traducción
automática a código ejecutable y fuerza a los programadores a tomar decisiones que no
aparecen reflejadas en ningún diagrama.
OMG (Object Management Group) es la principal representante del MDD y persigue solventar
algunos de los problemas fundamentales del uso de modelos, entre los que cabe destacar:
Según el grado de abstracción, MDA establece tres puntos de vista para un sistema
informático:
Dentro de los partidarios de MDA, existen dos corrientes [Hay04, McN03]: los
traduccionistas [MB02], que abogan por modelos capaces de producir aplicaciones completas
mediante un proceso de traducción automático, y los elaboracionistas [KWB03], que apuestan
por la producción parcial de aplicaciones. Para unos y otros, la primera opción para escribir
modelos es UML.
1. Extender UML desarrollando un nuevo perfil (profile) por medio del lenguaje de
restricciones OCL y los estereotipos (stereotypes) de UML. Un ejemplo notorio de
esta posibilidad es el perfil Executable UML [MB02], que permite la definición de
modelos computables y es de propósito general. Otros autores [Sil03, TSS04,
WZZ03], con el fin de simplificar los modelos, optan por definir perfiles
computables para dominios específicos.
Como puede comprobarse, los pasos 1, 2.1, 2.2, 2.3, 3.1 y 3.3 sirven para el
desarrollo de una línea de productos, mientras que los pasos 2.4, 2.5, 3.2, 3.4 y 3.5 se
ocupan del desarrollo de un producto particular de la línea. Pueden encontrase otros
procesos similares en [KMHC05] y [MA02].
Los modelos y los programas son especificaciones de software. Como indica, J. Greenfield
[GS04, páginas 226-229 y 318-319], la distinción entre “programa” y “modelo computable”
es muy difusa. A continuación, se muestra la debilidad de algunos criterios utilizados
habitualmente como discriminantes:
− La notación. Puede parecer que los lenguajes de programación tienen una notación
textual mientras que los lenguajes de modelado tienen una notación gráfica. Sin
embargo, existen lenguajes de programación gráficos, como el lenguaje que acompaña
los Lego Mindstorms [Min06], y lenguajes de modelado formales textuales, como Z
[Dil94], o semiformales con una fuerte componente textual, como el OCL de UML.
Los procesos de desarrollo incluidos en las secciones anteriores distinguen dos etapas
fundamentales28:
28 Análogas a los dos procesos básicos de desarrollo de un marco de trabajo descritos en la sección 2.2.3
Estado del arte 67
Factorías de software
GP [CE00] MDA [MSUW04]
[GS04]
Espacio de la solución
Plantilla de la factoría
Componentes No especificado
Componentes software
software
Lenguaje de modelado (especialización de
Espacio del problema Plantilla de la factoría UML para un dominio particular mediante
DSL DSL un perfil o creación de un nuevo lenguaje
con MOF)
Conocimiento de
Plantilla de la factoría
configuración Transformadores
Transformador
Transformador PIM → PSM → Código final
DSL → Código final
DSL → Código final
Figura 2.20. Equivalencia terminológica entre la GP, las factorías de software y MDA.
[ASU90, página 1]
29 A generator is a program that takes a higher-level specification of a piece of software and produces its implementation.
30 En las páginas 341-343 del mismo texto, K. Czarnecki amplía su definición indicando que, además de los
generadores de composición (compositional generators), que transforman en vertical (entre programas de distinto
nivel de abstracción), existen los generadores de transformación (transformational generators), que realizan
transformaciones horizontales (entre programas del mismo nivel de abstracción) y oblicuas (combinación de
transformaciones verticales y horizontales).
68 Capítulo 2
Una conversión entre modelos toma como punto de partida uno o varios modelos fuente y
produce como salida otro modelo objeto.31
[Gra04]
Una transformación es un proceso que, a partir de una o más especificaciones de entrada, crea
o modifica una o varias especificaciones de salida. Como las especificaciones de entrada se expresan en un
lenguaje del dominio de entrada, y las especificaciones de salida se expresan en un lenguaje del dominio de
salida, puede considerarse que una transformación es la creación de una instancia de una relación entre los
dominios de entrada y salida. Dicha relación se denomina conversión.33
31 A mapping between models is assumed to take one or more models as its input (these are the “sources”) and produce one
output model (this is the “target”).
32 Software restructuring techniques can be categorized as either horizontal or vertical. The research into horizontal
transformation concerns modification of a software artifact at the same abstraction level. This is the typical connotation when one
thinks of the term transformation. In contrast, vertical transformation is typically more appropriately called translation or
synthesis because a new artifact is being synthesized from a description at a different abstraction level.
33 A transformation is a process that creates or modifies one or more output specifications, from one or more input
specifications. Since each input specification is expressed in the language of an input domain, and each output specification is
expressed in the language of an output domain, we can think of a transformation as creating an instance of a relationship between
the input and out domains. This relationship is called a mapping.
Estado del arte 69
− Hay que evitar la manipulación directa del código objeto. Cualquier cambio se
especificará en el modelo fuente, que se volverá a traducir automáticamente para
actualizar el código objeto.
de algún GPL para el que haya disponible un compilador GPL → Código Máquina. De
esta manera, el trabajo se reduce a construir un compilador DSL → GPL (figura 2.22)34.
Si la sintaxis abstracta del DSL es sencilla, puede construirse su sintaxis concreta
respetando la sintaxis concreta del lenguaje de implementación del compilador
DSL → GPL. Es decir, para ahorrar la fase de análisis se busca embeber el DSL dentro
del lenguaje de implementación del compilador, de conseguir que el usuario tenga la
sensación de utilizar un DSL abstracto cuando en realidad escribe código en el mismo
lenguaje con que se implementa el compilador DSL → GPL. M. Fowler [Fow05]
denomina a este tipo de DSLs “internos”.
Figura 2.22. Compilador que utiliza código GPL como paso intermedio.
34En las figuras 2.23, 2.24 y 2.25, aparece en gris el software que debería desarrollar el autor de un
compilador DSL → Código Máquina.
Estado del arte 71
• El uso de grafos para representar los programas y de subgrafos para expresar los
patrones [AEHH96, BM03, VVP02].
− Parte declarativa. Está formada por un lenguaje de alto nivel de abstracción para
expresar relaciones (The Relations Language) y por un lenguaje base menos abstracto
semánticamente equivalente (The Core Language). Teóricamente, el usuario de QVT sólo
utiliza el lenguaje de relaciones, que se traduce automáticamente al lenguaje base.
− Parte imperativa. Para los casos en que es difícil especificar una transformación en
declarativo, QVT ofrece el lenguaje operacional de conversiones (The Operational
Mappings Language). Además, QVT permite la invocación de transformaciones externas
elaboradas con otros lenguajes mediante una interfaz de caja negra (Black Box
Implementations).
37 Algunos autores [MSUW04, páginas 52-53], utilizan el término “arquetipo” en lugar de “plantilla”.
74 Capítulo 2
3
Proceso EDD para el desarrollo
de familias de productos
When I'm doing templating I usually like to first write a hard coded class for a single case, get
that class working and debugged, and then (as gradually as I can) replace the hard coded
elements with templated elements.
Para que la obtención de los productos sea automática, resulta esencial que la
flexibilización del ejemplar sea formal. Este objetivo se podrá alcanzar en mayor o menor
medida según el grado de formalización del propio ejemplar. Por ejemplo, en el capítulo 2
se señaló que los productos del análisis y el diseño no suelen ser formales. Como resultado,
la derivación automática por analogía de dichos productos será muy limitada. En cambio,
los productos de la codificación y las pruebas, que suelen ser formales, sí se podrán obtener
76 Capítulo 3
automáticamente. Para que el lector se haga una idea de la potencia y versatilidad de EDD,
los capítulos 5 y 6 de esta tesis incluyen ejemplos de derivación automática por analogía de
programas escritos en Java, C++ y TRANSACT SQL; de juegos de prueba escritos en Java
y Modula-2; y de documentación escrita en HTML y Javadoc.
38En la sección 2.2 se revisan tres procesos de desarrollo de familias de productos que actualmente cuentan
con una gran difusión.
Proceso EDD para el desarrollo de familias de productos 77
- Su rango de variación.
- Si tiene dependencia con otros requisitos. Por ejemplo, si su valor se deriva del
valor de otros requisitos, si ciertas combinaciones de valores están prohibidas…
39 Este fenómeno se expone con claridad en [Cle01, sección 2.9, “A Balancing Act”].
Proceso EDD para el desarrollo de familias de productos 79
- Cada producto por separado. Los costes de cada producto pueden estimarse por
analogía con los costes del ejemplar, siguiendo las indicaciones de W. Myers
[Mye89].
3.2.2.1.1. Sintaxis
Generalmente, la sintaxis de un DSL se definirá con una gramática independiente del
contexto [ASU90] o un metamodelo [MOF06]. EDD propone el siguiente proceso para la
obtención sistemática de la sintaxis abstracta de un DSL, especificada con una gramática
independiente del contexto en notación EBNF (Extended Backus-Naur Form), a partir del
modelo FODA de un dominio. Con el propósito de aclarar este proceso se utilizará como
ejemplo el diagrama de la figura 3.3.
La principal razón por la que los DSLs pueden ser más abstractos que los GPLs es porque,
al restringir su área de aplicación a un dominio, se elimina la necesidad de describir los
requisitos fijos cada vez que se especifique un producto. Por ello, el primer paso de la
traducción FODA → EBNF será suprimir los nodos “fijos” del diagrama FODA, que se
determinarán del siguiente modo:
1) Todos los nodos hoja obligatorios (con cardinalidad [1..1]) son fijos.
2) Un nodo, que no sea hoja, es fijo si es obligatorio y todos sus descendientes son
fijos.
Por ejemplo, los nodos 2, 5, 11, 12, 17 y 18 de la figura 3.3 son fijos por ser hoja y
obligatorios. El nodo 10 también es fijo porque es obligatorio y todos sus descendientes,
los nodos 11 y 12, son fijos. Sin embargo, el nodo 1 no es fijo pues, a pesar de ser
obligatorio, su descendiente 3 no es fijo.
Figura 3.3. Ejemplo de modelo de un dominio. Figura 3.4. Modelo podado de un dominio.
40 En esta tesis las gramáticas se escribirán según la notación EBNF de ProGrammar [PGram06], un entorno
visual para el desarrollo de analizadores sintácticos (parsers). En ProGrammar :
FODA
EBNF
A ::= B A ::= [B] A ::= {B} A ::= [{B}] A ::= B | C A ::= B | C | B C A ::= [B] | [C]
Con el fin de facilitar la edición del código y mejorar su legibilidad, algunos autores
[Fow05, GS04, IS06, Jet06] defienden la oferta de distintas representaciones de un mismo
lenguaje orientadas a diferentes situaciones (por ejemplo, la posibilidad de que un programa
C++ pueda editarse como un documento XML o como un árbol sintáctico). Estos autores
suelen distinguir entre la sintaxis abstracta de un lenguaje, que caracteriza
conceptualmente los elementos del lenguaje y las reglas que rigen su combinación, y sus
sintaxis concretas, que definen diversas representaciones [GS04, páginas 280 y 281]. En
esta tesis se exploran las siguientes opciones a la hora de definir sintaxis concretas:
- Hacer coincidir la sintaxis concreta del DSL con la sintaxis del lenguaje con que
se implementa la flexibilización del ejemplar. Así, se evita el análisis léxico y
Proceso EDD para el desarrollo de familias de productos 83
- Hacer coincidir la sintaxis concreta del DSL con la sintaxis del lenguaje de
implementación del ejemplar. Esta posibilidad de “DSLs embebidos” facilita la
extensión del lenguaje de implementación del ejemplar. En las secciones 6.1, 6.4
y 6.5 se motiva y ejemplifica este tipo de DSLs.
3.2.2.1.2. Semántica
Para definir la semántica de un DSL, J. Greenfield y K. Short [GS04, páginas 309-318]
recomiendan dos alternativas:
a) Expresar una traducción del DSL a otro lenguaje cuya semántica esté bien
definida.
b) Expresar la semántica del DSL en función del hilo de ejecución de las sentencias
del DSL.
Cuando los productos de una familia y la flexibilización del ejemplar sean formales, la
semántica del DSL vendrá implícita en dicha flexibilización, que describirá como obtener
un producto final a partir de su especificación DSL (opción a).
- Fiabilidad [AV98]. Un lenguaje debe ofrecer mecanismos para detectar un mal uso
del mismo. Un ejemplo de mecanismo de control de errores, implantado en la
mayoría de los GPLs, es el sistema de tipos.
En la sección 2.1.3 se señaló que el código es el producto del ciclo de vida con mayor
tasa de reutilización. Por esta razón, las técnicas que comúnmente se emplean para
generalizar código y posibilitar su reutilización parecen buenas candidatas para la
flexibilización de un ejemplar. Concretamente, en esta tesis se examinará la viabilidad de las
técnicas recogidas en la figura 3.9. En nuestro estudio, distinguiremos entre técnicas
internas y externas.
- Las técnicas internas son aquellas que están disponibles en el lenguaje de codificación
del ejemplar. Por ejemplo, si el ejemplar es un programa escrito en Java, las técnicas
internas son la herencia de clases, el polimorfismo… Al utilizar técnicas internas se
aprovechan los medios del lenguaje para la detección automática de errores. Por
ejemplo, un “error de tipos” en la flexibilización anterior sería detectado y localizado
correctamente por el “compilador” de Java (javac). Lamentablemente, las técnicas
internas padecen los siguientes inconvenientes:
Genericidad Tipos 9 X 9
Subprogramas + sentencias
de selección
Comportamiento 9 X X
Estructura y
Composición de clases +
herencia de clases
comportamiento de una 9 X X
clase
Estructura y
Técnicas internas
Composición de clases +
genericidad
comportamiento de una 9 X 9
clase
Cabecera y
Sobrecarga de
subprogramas
comportamiento de un 9 9 9
subprograma
Estructura y
Herencia con
sobreescritura
comportamiento de una 9 9 X
clase
Orientación a aspectos Estructura y
(tejido dinámico de comportamiento de una 9 9 X
aspectos) clase
Estructura y
Orientación a aspectos
(tejido estático de aspectos)
comportamiento de una 9 9 9
clase
Plantillas con carga de Cualquier fragmento de
procesamiento de datos texto
X X 9
Técnicas externas
b) En muchas ocasiones, las técnicas internas sólo son capaces de ciertos tipos de
flexibilización. Además, entre las técnicas incluidas en un GPL suelen darse
solapamientos. Como consecuencia, muchas flexibilizaciones se complican
porque requieren el uso combinado de distintas técnicas o la elección entre
86 Capítulo 3
técnicas alternativas, sin contar, en muchas ocasiones, con unos criterios claros
para escoger la técnica más adecuada41.
- Las técnicas externas flexibilizan un ejemplar por generación de código. Por ejemplo,
la generalización de un programa escrito en Java puede conseguirse utilizando el GPL
Ruby y su librería de plantillas ERB [ERB06]. Las ventajas e inconvenientes de las
técnicas externas son las contrarias a las ventajas e inconvenientes de las técnicas
internas. Las técnicas externas son aplicables a cualquier producto software, manejan la
variabilidad entre programas en tiempo de generación y son capaces de cualquier
flexibilización. Sin embargo, los medios de que disponen para el control de errores de
flexibilización son muy limitados.
41La dificultad de escoger la técnica de generalización más conveniente para un problema dado se ilustra en el
capítulo 6 de [Cop99], donde se trata de sistematizar esta elección.
42 QVT también padece esta limitación, pues sólo es capaz de manipular documentos XMI.
Proceso EDD para el desarrollo de familias de productos 87
Por otro lado, así como el desarrollo por refinamientos sucesivos [Wir71] utiliza la
idea de “descomposición” para construir operaciones complejas dividiéndolas
progresivamente hasta alcanzar operaciones elementales, EDD plantea la siguiente
estrategia de “analogías sucesivas”: por definición, cualquier ejemplar satisface los
requisitos fijos de una familia de productos, sin embargo, con un requisito variable pueden
darse las siguientes situaciones:
4
Lenguaje de transformaciones ETL
B.L.Whorf
El lenguaje ETL es el medio que propone esta tesis para flexibilizar ejemplares. Se trata de
una técnica externa, modular, no invasiva y aplicable a cualquier producto software.
43 Como se verá en la sección 4.2.2, la escritura de un analizador para el ejemplar será normalmente
innecesaria, ya que la implementación Ruby de ETL aprovecha las expresiones regulares.
90 Capítulo 4
4.1.1. Primitivas
Para desarrollar un generador ETL, se dispone de las siguientes primitivas:
Cuando la implementación del ejemplar sea modular y esté repartida entre varios
ficheros, con frecuencia será necesario expresar una misma sustitución sobre más de un
fichero. Para facilitar la reutilización de las sustituciones y evitar así su repetición, las
sustituciones son independientes de los ficheros del ejemplar y de los ficheros del
producto final. Una sustitución se liga a los ficheros del ejemplar y del producto final
mediante la primitiva de “producción”, que se explica a continuación.
Una sustitución puede ser local o global. Cuando sea local, se aplicará
exclusivamente sobre la primera ocurrencia de su patrón de código en el fichero del
ejemplar. Cuando sea global, se aplicará sobre todas las ocurrencias.
b. Suma (G1, G2). Obtiene un nuevo generador cuyas sustituciones y producciones son
la unión de las sustituciones y producciones de G1 y G2.
c. Superposición (G1, G2). Actualiza las sustituciones y las producciones de G1 con las
de G2. Las que tienen el mismo nombre se "sobrescriben", y las que no, se añaden.
4.1.4. Ejemplo
Para ilustrar lo explicado, se utilizará un ejemplo planteado por B. Eckel en [Eck03]. Se
trata de un programa escrito en Java que emula el reciclaje de basura. El programa recibe
un cubo de basura con residuos de papel y aluminio mezclados, y un par de cubos donde
deberá separarlos. Cada residuo tiene un peso y un valor de venta según su tipo (el papel se
revende a 0.10 y el aluminio a 1,67). La figura 4.3 es el diseño del citado programa y las
figuras 4.4, 4.5, 4.6 y 4.7 son la codificación. La figura 4.8 es la clase RecycleTest, que
aprovecha el marco de trabajo JUnit [Jun06] para probar el programa de reciclaje. La figura
4.9 muestra un ejemplo de ejecución de RecycleTest.
import java.util.*;
import java.io.*;
import java.util.*;
import java.io.*;
class Recycle {
public Recycle(ArrayList bin, ArrayList AluminumBin, ArrayList PaperBin){
Iterator sorter = bin.iterator();
while(sorter.hasNext()) {
Object t = sorter.next();
if(t instanceof Aluminum) AluminumBin.add(t);
if(t instanceof Paper) PaperBin.add(t);
}
}
}
import junit.framework.*;
import java.util.*;
Time: 0,701
OK (1 test)
<specification>
Specification ::= OutDir {Trash};
<outDir value="generation"/>
OutDir ::= '[a-zA-Z]+';
<trash name="Glass" value="1.08"/>
Trash ::= Name Value;
<trash name="Plastic" value="0.24"/>
Name ::= '[a-zA-Z]+';
<trash name="Cardboard" value="0.8"/>
Value ::= '[0-9]+(\.[0-9]+)';
</specification>
Figura 4.10. Gramática abstracta de un DSL para Figura 4.11. Ejemplo de especificación XML de un
especificar programas de reciclaje de basura. programa de reciclaje de basura.
- Producción 1. Expresa cómo obtener una clase Trash objeto a partir de la clase
Trash del ejemplar. Como las clases Trash de todos los programas objeto coinciden,
la producción carece de sustituciones. Sus parámetros son:
46En [Eck03] se flexibiliza el programa explotando los recursos de la orientación a objetos. En [HEAC05] se
justifica porqué la flexibilización ETL es más ventajosa que la propuesta por B. Eckel.
Lenguaje de transformaciones ETL 97
- Producción 3. Expresa como obtener una clase Recycle objeto a partir de la clase
Recycle del ejemplar. Dispone de dos sustituciones que indican cómo adaptar el
código variable de la figura 4.7, resaltado en los colores y . Sus parámetros son:
Figura 4.12. Ejemplo de generación del programa objeto especificado en la figura 4.11.
enriquece L1’ con nuevas instrucciones. Este proceso se repite hasta que L1n=L1. [ASU90, páginas 743-747;
Cle01, páginas 340-347]
Antes de definir una producción, prod verificará la ausencia de colisiones entre sus
sustituciones. Si se da alguna colisión, se lanzará la excepción correspondiente.
50No dar valor a subList es equivalente a escribir subList = ‘all’. Esta segunda modalidad se utilizará
cuando se desee incluir en una producción todas las sustituciones previamente definidas en el generador y,
además, se quiera dar valor a name.
Lenguaje de transformaciones ETL 101
- gen(prodList)
Antes de definir una generación, gen verificará la ausencia de colisiones entre sus
producciones. Si se da alguna colisión, se lanzará la excepción correspondiente.
b. Suma. Para la suma de generadores, Generator dispone del operador + y del método
add:
- +(generator)
- add(generator)
- <<(generator)
- sup!(generator)
52 Siguiendo la notación estándar de Ruby, los métodos auxiliares prod?, gen?, add?, sup? y crash?
llevan el sufijo ? para indicar que devuelven booleanos.
Lenguaje de transformaciones ETL 103
4.2.3. Ejemplo
A continuación, se codifica la flexibilización de la familia “programas de reciclaje de
basura” planteada en la sección 4.1.4.
- Trashes. Tabla que almacena los pares tipo de basura – precio de reventa.
require 'rexml/document'
def readSpecification
doc = REXML::Document.new(File.open("specification.xml"))
out_dir = ''
doc.root.each_element("outDir") { |e|
out_dir = e.attributes["value"]
}
trashes = {}
doc.root.each_element("trash") { |e|
trashes.store(e.attributes["name"], e.attributes["value"])
}
require '..\ETL\ETL'
require '..\ETL\ETL'
# Production 1
prod(EXEMPLAR_DIR + '\Trash.java', out_dir+'\Trash.java', [], ‘1’)
# Productions 2
trashes.each { |kind, value|
gsub(/Paper/, kind, kind)
sub(/\d+.\d\d/,
value,
"#{kind}_value")
prod("#{EXEMPLAR_DIR}" + "\\Paper.java",
"#{out_dir}\\#{kind}.java",
Lenguaje de transformaciones ETL 105
[kind, "#{kind}_value"],
"2_#{kind}")
}
# Production 3
sub(/ArrayList .+ Bin(,ArrayList .+Bin)*/x,
trashes.keys.collect {|kind|
"ArrayList " + kind + "Bin"
}.join(','),
'argRecycle')
sub(/(if.+;\s*)+/,
trashes.keys.collect {|kind|
"if(t instanceof #{kind}) " + "#{kind}Bin.add(t);\n"
}.join,
'ifRecycle')
prod("#{EXEMPLAR_DIR}" + "\\Recycle.java",
"#{out_dir}\\Recycle.java",
['argRecycle', 'ifRecycle'] ,
'3')
end
end #RecycleGen
require '..\ETL\ETL'
gsub(/aluminumBin, paperBin/,
trashes.keys.collect {|kind|
"#{kind.downcase}Bin"
}.join(', '))
sub(/
(
\s*case\s*\d+\s*:\s*
bin.add\(new\s*\w+\(Math\.random\(\)\s*\*\s*100\)\);\s*
break;\s*
)+
/xm,
caseTemplate(trashes)
)
sub(/(\s*Trash.sumValue\(\w+Bin.+\)\s*(\+)?\s*)+/,
sumValueTemplate(trashes))
def caseTemplate(trashes)
code = ''
index = 0
trashes.each_key {|kind|
code += "case #{index}:
bin.add(new #{kind}(Math.random() * 100));
break;\n"
index += 1
}
return code
106 Capítulo 4
end
def sumValueTemplate(trashes)
code = ''
index = 0
trashes.each_key {|kind|
code += " + " if index > 0
code += "Trash.sumValue\(#{kind.downcase}Bin.iterator\(\)\)"
code += "\n"
index += 1
}
return code
end
end #TestGen
sub(/\d+.\d\d/,
value,
"#{kind}_value")
prod("#{EXEMPLAR_DIR}" + "\\Paper.java",
"#{out_dir}\\#{kind}.java",
[kind, "#{kind}_value"],
"2_#{kind}")
}
recorre la tabla trashes, recuperando en cada iteración los campos kind y value.
Con estos campos:
ii. Se define una sustitución local, cuyo nombre es el valor de kind más el sufijo
_value y que modifica el código variable marcado en en la figura 4.5.
iii. Se define una producción, cuyo nombre es 2_ más el valor de kind, que
incluye las sustituciones definidas en i e ii.
53Con el propósito de evitar errores en la escritura de bucles, Ruby ofrece iteradores para todos sus
contenedores predefinidos.
Lenguaje de transformaciones ETL 107
- keys: proporciona una lista formada por las claves de una tabla. En el ejemplo
de la figura 4.11, trashes.keys devolvería [Glass, Plastic, Cardboard].
trashes.keys.collect {|kind|
"ArrayList " + kind + "Bin"
}
trashes.keys.collect {|kind|
"ArrayList " + kind + "Bin"
}.join(',')
producirá la cadena
require '..\ETL\ETL'
EXEMPLAR_DIR = 'exemplar'
5
Estudio comparativo de
metodologías y técnicas de
desarrollo de familias de productos
The history of programming is an exercise in hierarchical abstraction. In each generation, language designers
produce constructs for lessons learned in the pervious generation, and then architects use them to build more
complex and powerful abstractions.
J. Smith, D. Stotts. Elemental Design Patterns: A Link Between Architecture and Object Semantics.
El presente capítulo muestra, a través de dos ejemplos, distintas maneras de construir una
familia de productos.
Figura 5.1. Versión reducida de un diccionario para la revisión ortográfica de documentos en castellano.
- Requisitos fijos: los diccionarios serán clases Java con un método isWord para
verificar la pertenencia de una palabra al lenguaje correspondiente.
import java.util.*;
dictionary = language.getWords();
public class English implements Language { public class Modula2 implements Language {
Man FOR
dog IF
Diccionario ::= Lexico; school IMPORT
Lexico ::= {alpha}54; cow BEGIN
… …
import java.util.*;
import java.io.*;
BlackBoxGeneralizedDictionary(String languageFile) {
this.languageFile = languageFile;
analyzeLanguage();
}
//////////////////////////////////////////////////////
// Analyzer
//////////////////////////////////////////////////////
public void analyzeLanguage() {
try {
BufferedReader f = new BufferedReader(
new FileReader(languageFile));
String word = null;
while ((word = f.readLine()) != null) {
dictionary.addElement(word);
}
} catch (Exception e) {
System.err.println("Unable to read the language file");
}
}
//////////////////////////////////////////////////////
// Executor
//////////////////////////////////////////////////////
public boolean isWord(String word) {
for (int i=0; i<dictionary.size(); i++) {
String wordAux = (String) dictionary.elementAt(i);
if (word.equals(wordAux))
return true;
}
return false;
}
}
cada vez que se instancie un diccionario se invertirá un tiempo inútil en leer y cargar en
memoria la especificación DSL correspondiente. Esto puede evitarse haciendo que la
infrestructura sea un compilador que genere diccionarios específicos. Las secciones 5.1.2.2
y 5.1.2.3 presentan dos enfoques alternativos para la generación: la escritura desde cero
sobre un flujo de texto y la flexibilización del ejemplar con ETL.
Figura 5.12. Arquitectura de un compilador de diccionarios. Generación desde cero, escribiendo sobre un flujo de
texto.
import java.io.*;
import java.util.*;
class ClassicTransformer {
private Vector dictionary = new Vector();
private String languageFile;
private String language;
ClassicTransformer(String languageFile) {
this.languageFile = languageFile;
StringTokenizer st =
new StringTokenizer(languageFile, ".");
language = st.nextToken();
analyzeLanguage();
generateDictionary();
}
//////////////////////////////////////////////////////
// Analyzer
//////////////////////////////////////////////////////
public void analyzeLanguage() {
try {
BufferedReader f = new BufferedReader(
new FileReader(languageFile));
String word = null;
while ((word = f.readLine()) != null) {
dictionary.addElement(word);
}
} catch (Exception e) {
System.err.println(
"Unable to read the dictionary specification file");
}
}
//////////////////////////////////////////////////////
// Generator
116 Capítulo 5
//////////////////////////////////////////////////////
public void generateDictionary() {
try {
BufferedWriter f = new BufferedWriter(
new FileWriter(language+"Dictionary.java"));
f.write("public class "+language+"Dictionary {\n");
f.write(" public boolean isWord(String word) {\n");
f.write(" if (\n");
for (int i=0; i<dictionary.size()-1; ++i) {
f.write(" (word == \""+dictionary.elementAt(i)+"\") ||\n");
}
f.write(" (word == \""+dictionary.elementAt(dictionary.size()-1)+"\")\n");
f.write(" )\n");
f.write(" return true;\n");
f.write(" else\n");
f.write(" return false;\n");
f.write(" }\n");
f.write("}\n");
f.close();
} catch (Exception e) {
System.err.println("Unable to generateDictionary file Static"+
language+"Dictionary.java");
}
}
}
Figura 5.13. Codificación de un compilador de diccionarios. Generación desde cero, escribiendo sobre un flujo de
texto.
Las figuras 5.14 y 5.15 son los diccionarios generados a partir de las especificaciones
de las figuras 5.8 y 5.9 respectivamente.
Figura 5.14. Diccionario de inglés generado por la Figura 5.15. Diccionario deModula-2 generado por
infraestructura de la figura 5.13 a partir de la la infraestructura de la figura 5.13 a partir de la
especificación de la figura 5.8. especificación de la figura 5.9.
Las figuras 5.16, 5.17 y 5.18 son, respectivamente, el diseño arquitectónico, el diseño
detallado y la codificación de una flexibilización con ETL del ejemplar de la figura 5.1.
Como ilustra la figura 5.17, el generador ETL (la clase DictionaryGenerator) consta de dos
sustituciones, que ajustan el nombre de la clase diccionario (en rosa) y la expresión
condicional de la sentencia de selección (en azul), y de una producción que aplica las
sustituciones sobre el ejemplar para producir las clases Java resultado.
Figura 5.16. Arquitectura de un compilador de diccionarios. Generación con ETL por analogía.
Figura 5.17. Diseño detallado de un compilador de diccionarios. Generación con ETL por analogía.
require '..\..\..\..\ETL\ETL'
###########################################################
# Generator
###########################################################
118 Capítulo 5
unless ARGV[0]
print "You should specify the dictionary file"
exit
end
languageFile = ARGV[0]
###########################################################
# “Analyzer”
###########################################################
eval "@dictionary = [#{File.open("#{languageFile}").read}]"
###########################################################
# Running the generator
###########################################################
DictionaryGenerator.new(languageFile.split(/\./)[0], @dictionary).gen
Figura 5.18. Codificación de un compilador de diccionarios. Generación con ETL por analogía.
La figura 5.21 muestra, paso a paso, cómo será ejecutada la sentencia anterior por el
intérprete de Ruby, suponiendo que la variable languageFile valga “English.txt”
(figura 5.20).
Figura 5.19. Gramática, equivalente a la de la figura 5.7, Figura 5.20. Especificación, según la gramática de la
de un DSL interno a Ruby figura 5.18, de un diccionario de inglés.
Aclaraciones
4 Como resultado de la ejecución de eval, existe en memoria una variable, llamada @dictionary, cuyo
contenido es ["man", "dog", "school", "cow"]
Figura 5.21. Ejecución, paso a paso, del análisis interno realizado por el intérprete de Ruby de la especificación de la
figura 5.20.
EDD con la citada metodología. Además, el ejemplo servirá para examinar cómo aplicar las
técnicas comunes de generalización de código a la flexibilización de un ejemplar y ver
cuáles son sus limitaciones.
- Los elementos pueden almacenarse en una lista según las siguientes modalidades
(Ownership):
La lista guarda copias de sus elementos y, en caso de que la lista se borre, ésta se
responsabiliza de liberar la memoria que ocupan las copias (Copy).
- Para facilitar la depuración de productos, una lista puede imprimir en consola cualquier
llamada a sus métodos (Tracing).
56En la figura 5.22, los corchetes alrededor de una característica ([ElementType], [LengthType]) señalan que la
característica admite un rango de valores potencialmente infinito.
Estudio comparativo de metodologías y técnicas de desarrollo de familias de productos 121
Según los autores, el nodo List de la figura 5.22 sugiere la responsabilidad de almacenar
elementos, Ownership las responsabilidades de copiar y liberar elementos, LengthCounter la
responsabilidad de contar el número de elementos de una lista y Tracing la responsabilidad
de imprimir mensajes que señalen la ejecución de los métodos de una lista.
Para cada responsabilidad, se determinará una categoría de componentes que cubran las
alternativas descritas en el modelo del dominio. Por ejemplo, los tipos de pertenencia de un
elemento a una lista (Ownership) se cubrirán con las categorías de componentes Copier y
Destroyer del siguiente modo: External reference → EmptyCopier & EmptyDestroyer; Owned
reference → EmptyCopier & ElementDestroyer; Copy → ElementCopier & ElementDestroyer.
122 Capítulo 5
Figura 5.23. Correspondencias entre los nodos del diagrama de la figura 5.22, las responsabilidades que debe asumir
la línea de productos y las categorías de componentes que deben desarrollarse.
Según los autores, las categorías de componentes Counter y Tracing utilizarán la categoría
BasicList, que a su vez utilizará a Copier y a Destroyer (ver figura 5.24).
Teniendo en cuenta que una capaN utilizará los servicios de la capaN-1 y proveerá de
servicios a la capaN+1, se sitúan las categorías de componentes respetando las dependencias
de uso antes identificadas (figura 5.25).
Puesto que no hay dependencia entre Tracing y Counter, es indiferente situar Tracing
sobre Counter o Counter sobre Tracing. En la figura 5.25, el trazo discontinuo alrededor de
Tracing y de Counter indica que son capas opcionales.
Según los autores, las categorías de componentes más pequeños y la gestión de los
tipos se describirán conjuntamente en un “almacén de configuración” (configuration repository)
que ocupará la capa inferior de la arquitectura. A continuación, se determinará qué
categoría de componentes es la principal. Lamentablemente, los autores no aportan ningún
criterio para dicha determinación. En nuestro ejemplo, la categoría principal de
componentes es BasicList. Como se verá en el punto III, los componentes que ocupan las
capas superiores a la categoría principal de componentes se implementarán con herencia,
mientras que los componentes de las capas inferiores se implementarán con composición.
La variabilidad acerca de los tipos se implementará mediante genericidad, haciendo que los
componentes TracedList, LengthList, PtrList, ElementCopier, EmptyCopier, ElementDestroyer y
EmptyDestroyer sean plantillas (templates). Para evitar una enumeración exhaustiva de todos
los tipos (en el ejemplo, ElementType y LengthType) cada vez que se define o utiliza una
plantilla, toda la información sobre los tipos se aglutinará en el almacén de configuración57.
Tracing: TracedList
Counter: LengthList
BasicList: PtrList
ConfigurationRepository
ElementType: [ElementType]
Copier: ElementCopier | EmptyCopier
Figura 5.24. Dependencias de uso Destroyer: ElementDestroyer | EmptyDestroyer
LengthType: int | short | long | …
entre las categorías de componentes.
Figura 5.25. Organización de las categorías de componentes en
una arquitectura por capas.
GenVoca es una propuesta de D. Batory et al. [BCRW98, BO92] para construir generadores
de aplicaciones componiendo capas de abstracción orientadas a objetos. Posteriormente,
GenVoca ha evolucionado al modelo AHEAD [Bat04, BSR04]. En nuestro ejemplo, la
gramática de la figura 5.26 especifica cómo pueden combinarse los componentes de las
categorías. Por ejemplo, una lista (List) puede obtenerse combinando una TracedList con
una OptCounterList o, simplemente, a partir de una OptCounterList, que a su vez puede
obtenerse combinando una LengthList con una BasicList o a partir de una BasicList…
Figura 5.26. Gramática GenVoca que especifica las posibles combinaciones entre componentes.
Figura 5.27. Diseño detallado, obtenido con la metodología propuesta en [CE00], de la línea de productos “listas
para C++”.
#include <iostream>
using namespace std;
///////////////////////////////////////////////////////
// TracedList /////////////////////////////////////////
///////////////////////////////////////////////////////
template<class BaseList>
class TracedList: public BaseList
{
public:
typedef typename BaseList::Config Config;
private:
typedef typename Config::ElementType ElementType;
typedef typename Config::ReturnType ReturnType;
public:
TracedList(ElementType& h, ReturnType *t = 0) :
BaseList(h, t)
{}
126 Capítulo 5
void setHead(ElementType& h)
{
cout << "setHead(" << h << ")" << endl;
BaseList::setHead(h);
}
ElementType& head()
{
cout << "head()" << endl;
return BaseList::head();
}
///////////////////////////////////////////////////////
// LengthList /////////////////////////////////////////
///////////////////////////////////////////////////////
template<class BaseList>
class LengthList : public BaseList
{
public:
typedef typename BaseList::Config Config;
private:
typedef typename Config::ElementType ElementType;
typedef typename Config::ReturnType ReturnType;
typedef typename Config::LengthType LengthType;
public:
LengthList(ElementType& h, ReturnType *t = 0) :
BaseList(h, t), length_(computedLength())
{}
private:
LengthType computedLength() const
{ return tail()?tail()->length()+1:1; }
LengthType length_;
};
///////////////////////////////////////////////////////
// Copier /////////////////////////////////////////////
///////////////////////////////////////////////////////
template<class ElementType>
struct ElementCopier
{
static ElementType* copy(const ElementType& e)
{ return new ElementType(e); }
};
template<class ElementType>
struct EmptyCopier
Estudio comparativo de metodologías y técnicas de desarrollo de familias de productos 127
{
static ElementType* copy(ElementType& e)
{ return &e; }
};
///////////////////////////////////////////////////////
// Destroyer //////////////////////////////////////////
///////////////////////////////////////////////////////
template<class ElementType>
struct ElementDestroyer
{
static void destroy(ElementType *e)
{ delete e; }
};
template<class ElementType>
struct EmptyDestroyer
{
static void destroy(ElementType *e)
{}
};
///////////////////////////////////////////////////////
// PtrList ////////////////////////////////////////////
///////////////////////////////////////////////////////
template<class Config_>
class PtrList
{
public:
typedef Config_ Config;
private:
typedef typename Config::ElementType ElementType;
typedef typename Config::ReturnType ReturnType;
typedef typename Config::Destroyer Destroyer;
typedef typename Config::Copier Copier;
public:
PtrList(ElementType& h, ReturnType *t = 0) :
head_(0), tail_(t)
{ setHead(h); }
~PtrList()
{ Destroyer::destroy(head_); }
void setHead(ElementType& h)
{
head_ = Copier::copy(h);
}
ElementType& head()
{ return *head_; }
private:
ElementType* head_;
ReturnType* tail_;
};
Figura 5.28. Codificación de la línea de productos “listas para C++” obtenida con la metodología propuesta en
[CE00].
128 Capítulo 5
V. Ingeniería de aplicación
Aunque en las páginas 586-590 de [CE00] se indica cómo ocultar los detalles de
implementación de la línea de productos con un DSL, por brevedad se obviará este paso.
Sin considerar la existencia del citado DSL, la especificación de un producto se realizaría
describiendo el almacén de configuración correspondiente. Por ejemplo, la figura 5.29 es
un almacén que describe una lista que guarda elementos de clase MyClass, con pertenencia
de tipo Copy, que dispone de un contador de tipo short para conocer la longitud de la lista y
que imprime en la consola mensajes que señalan la ejecución de los métodos de la lista. La
figura 5.30 muestra la combinación de los componentes del espacio de la solución que
produce la lista anterior.
struct TracedCopyMyClassLenListConfig
{
typedef MyClass ElementType;
typedef short LengthType;
typedef ElementDestroyer<ElementType> Destroyer;
typedef ElementCopier<ElementType> Copier;
typedef
TracedList<
LengthList<
PtrList<TracedCopyMyClassLenListConfig>
>
> ReturnType;
};
typedef Figura 5.30. Combinación de las clases de la
TracedCopyMyClassLenListConfig::ReturnType
TracedCopyMyClassLenList; figura 5.28, que satisface la especificación de la
figura 5.29.
Figura 5.29. Ejemplo de almacén de configuración.
3) Todos los nodos hoja obligatorios (con cardinalidad [1..1]) son fijos.
4) Un nodo, que no sea hoja, es fijo si es obligatorio y todos sus descendientes son
fijos.
En el diagrama de la figura 5.22 no hay ningún nodo fijo, ya que los corchetes
alrededor de ElementType y LengthType indican grupos or exclusivos de un número
potencialmente infinito de valores (figura 5.31).
Siguiendo las pautas descritas en la sección 3.2.1.6, se escogerán, por ejemplo, los
requisitos variables que aparecen en negro en la figura 5.32. A continuación, se diseñará,
codificará y probará un ejemplar que satisfaga dichos requisitos. La figura 5.33 es la
codificación del ejemplar que se utilizará en el resto de la sección. El código en blanco
implementa los requisitos fijos del dominio, mientras que el código de color , , y
implementa los requisitos variables.
Figura 5.32. Requisitos de la familia “listas para C++” seleccionados para el desarrollo de un ejemplar.
class List
{
130 Capítulo 5
private:
MyClass* head_;
List* tail_;
int length_;
public:
List(MyClass&h, List *t=0):
head_(0), tail_(t), length_(computedLength())
{ setHead(h); }
~List()
{ delete head_; }
void setHead(MyClass& h)
{
cout << "setHead(" << h <<")" << endl;
head_ = new MyClass(h);
}
MyClass& head()
{
cout << "head()" << endl;
return *head_;
}
private:
int computedLength() const
{ return tail()?tail()->length()+1:1; }
};
Funcionalidad fija de todas las listas ElementType Ownership LengthCounter Tracing
En nuestro ejemplo no hay nada que podar pues, como se explicó anteriormente, el
diagrama FODA carece de nodos fijos.
List ::= OutFile "," ElementType "," Ownership ["," LengthCounter] ["," Tracing];
OutFile ::= "\"Out File\"" "=>" quotedstring;
ElementType ::= "\"Element Type\"" "=>" quotedstring;
Ownership ::= "\"Ownership\"" "=>"
("\"External reference\"" | "\"Owned reference\"" | "\"Copy\"");
LengthCounter ::= "\"Length Counter\"" "=>" ("\"yes\"" "," LengthCounterType | "\"no\"");
LengthCounterType ::= "\"Length Counter Type\"" "=>" ("\"short\"" | "\"int\"");
Tracing ::= "\"Tracing\"" "=>" ( "\"yes\"" | "\"no\"" );
Las figuras 5.38 y 5.39 son el diseño y la codificación ETL de la flexibilización del
ejemplar. Los generadores ElementType, Ownership, LengthCounter y Tracing implementan los
requisitos variables {ElementType}, {Ownership}, {LengthCounter, LengthType} y {Tracing},
adaptando el ejemplar List de la figura 5.33 y produciendo, como resultado, una nueva lista
NewList.
def ownedReference
sub(/new Exemplar\(h\)/ , '&h')
end
def copy
end
end
Estudio comparativo de metodologías y técnicas de desarrollo de familias de productos 133
- Es muy concisa.
58La sección 5.1.2.3 explica como analizar en Ruby una especificación, escrita en un DSL interno, con el
procedimiento eval.
134 Capítulo 5
unless ARGV[0]
print "You should specify the list specification file"
exit
end
# analyzer
eval "@list_specificacion = {#{File.open("#{ARGV[0]}").read}}"
# running generators
ElementType.new('List.cpp', @list_specificacion['Out File'],
@list_specificacion['Element Type']).gen
(
Ownership.new(@list_specificacion['Out File'], @list_specificacion['Out File'],
@list_specificacion['Ownership'])
+
LengthCounter.new(@list_specificacion['Out File'], @list_specificacion['Out File'],
@list_specificacion['Length Type'])
+
Tracing.new(@list_specificacion['Out File'], @list_specificacion['Out File'],
@list_specificacion['Tracing'])
).gen
Figura 5.40. Análisis de las especificaciones DSL y ejecución coordinada de los generadores ETL.
En la sección 5.1.1 se utilizó la herencia de clases para flexibilizar tipos. Con esta
técnica, la parametrización se resuelve en tiempo de ejecución por enlace dinámico, lo que
puede ralentizar innecesariamente los productos. En su lugar, en las secciones 5.2.3.1-
5.2.3.8, se utilizará genericidad, que resuelve la parametrización en tiempo de compilación.
Estudio comparativo de metodologías y técnicas de desarrollo de familias de productos 135
La figura 5.42 es una flexibilización obtenida aplicando esta técnica. Para configurar
el comportamiento de una lista, se utilizará un registro de tipo BehaviorSpecification, definido
en la figura 5.43. La figura 5.44 especifica una lista equivalente a la de la figura 5.29
instanciando un registro BehaviorSpecification.
BehaviorSpecification bs_;
public:
List(BehaviorSpecification bs, ElementType&h, List *t=0):
head_(0), tail_(t)
{
bs_ = bs;
if (bs_.lengthCounter)
length_ = computedLength();
setHead(h);
}
~List()
{
if ((bs_.ownership == OwnedReference) || (bs_.ownership == Copy))
delete head_;
}
void setHead(ElementType& h)
{
if (bs_.tracing)
cout << "setHead(" << h <<")" << endl;
if (bs_.ownership == Copy)
head_ = new ElementType(h);
else
head_ = &h;
}
ElementType& head()
{
if (bs_.tracing)
cout << "head()" << endl;
return *head_;
}
length_ = computedLength();
}
private:
int computedLength() const
{ return tail()?tail()->length()+1:1; }
};
Figura 5.43. Registro para especificar listas según la flexibilización de Figura 5.44. Especificación de una lista,
la figura 5.42. lograda parametrizando el registro de la
figura 5.43.
− Mezcla los códigos fijo y variable (ver colores en la figura 5.42), lo que provoca
acoplamientos código fijo ↔ código variable y código variable ↔ código variable.
Las próximas secciones mostrarán como obtener una flexibilización modular del
ejemplar. Las secciones 5.2.3.2-5.2.3.4 utilizarán técnicas invasivas, que introducen en el
ejemplar “etiquetas” que apuntan a los módulos encargados de gestionar cada requisito
variable (figura 1.4). Las secciones 5.2.3.5-5.2.3.8 mostrarán el uso de técnicas no invasivas,
con las que el ejemplar se mantendrá intacto y donde cada módulo se encargará, además de
implementar el requisito variable correspondiente, de engancharse al ejemplar (figura 1.4).
Estudio comparativo de metodologías y técnicas de desarrollo de familias de productos 137
5.2.3.2. Subprogramas
Exceptuando la variabilidad de los tipos (ElementType y LengthType) que, como se dijo
anteriormente, se flexibilizará con genericidad, cada requisito variable puede implementarse
con un subprograma (ver figura 5.45).
////////////////////////////////////////////////
// List
////////////////////////////////////////////////
BehaviorSpecification bs_;
enum Method {Constructor, Destructor, SetHead, Head, SetTail, Tail};
public:
List(BehaviorSpecification bs, ElementType&h, List *t=0):
head_(0), tail_(t)
{
bs_ = bs;
lengthCounter(Constructor);
setHead(h);
}
~List()
{
ownership(*head_, Destructor);
}
void setHead(ElementType& h)
{
tracing(h, SetHead);
ownership(h, SetHead);
}
ElementType& head()
{
tracing(*head_, Head);
return *head_;
}
List *tail()
{
tracing(*head_, Tail);
return tail_;
}
private:
138 Capítulo 5
///////////////////////////////////////////////
// Ownership
////////////////////////////////////////////////
void ownership(ElementType& h, Method caller)
{
switch(bs_.ownership) {
case ExternalReference: if (caller = SetHead) head_ = &h;
case OwnedReference:
switch(caller) {
case Destructor: delete head_;
case SetHead: head_ = &h;
}
case Copy:
switch(caller) {
case Destructor: delete head_;
case SetHead: head_ = new ElementType(h);
}
}
}
///////////////////////////////////////////////
// LengthCounter
////////////////////////////////////////////////
void lengthCounter(Method caller)
{
if (bs_.lengthCounter)
computedLength();
}
int computedLength()
{ return tail()?tail()->length()+1:1; }
///////////////////////////////////////////////
// Tracing
////////////////////////////////////////////////
void tracing(ElementType& h, Method caller)
{
if (bs_.tracing)
switch(caller) {
case SetHead: cout << "setHead(" << h <<")" << endl;
case Head: cout << "head()" << endl;
case SetTail: cout << "setTail(t)" << endl;
case Tail: cout << "tail()" << endl;
}
}
};
5.2.3.3. Composición
Las figuras 5.46 y 5.47 son el diseño y la codificación de una flexibilización donde al
ejemplar (la clase List) se le ha quitado el código variable y se le ha introducido unos
atributos (ownership_, tracing_ y lengthCounter_) y unas “etiquetas” (ver código de color , y
dentro de la clase List) que enlazan con las clases que implementan los requisitos
variables. El diseño sigue el patrón strategy [GHJV94], agrupando las distintas posibilidades
de variación con herencia de interfaz (Ownership agrupa a ExternalReference, OwnedReference y
Copy, Tracing agrupa a WithTracing y WithoutTracing, y Length agrupa a WithLength y
WithoutLength). La modalidad de una lista se configura en tiempo de ejecución, pasando al
constructor de la lista objetos de tipo Ownership, Tracing y Length para que éste actualice los
atributos ownership_, tracing_ y lengthCounter_. Lamentablemente, la “ejecución de las
etiquetas de enlace”, que consistirá en la invocación a un método de los atributos
ownership_, tracing_ o lengthCounter_, se resolverá con enlace dinámico, lo que ralentizará las
listas. La figura 5.48 es la especificación de una lista con pertenencia de tipo Copy, de la que
puede conocerse su longitud, y capaz de imprimir mensajes de depuración.
Figura 5.46. Diseño de una flexibilización del ejemplar basada en la composición y la herencia de clases.
////////////////////////////////////////////////
// Ownership
////////////////////////////////////////////////
////////////////////////////////////////////////
// Tracing
////////////////////////////////////////////////
void head() {
cout << "head()" << endl;
}
void setTail() {
cout << "setTail(t)" << endl;
}
void tail() {
cout << "tail()" << endl;
}
};
////////////////////////////////////////////////
// LengthCounter interface
////////////////////////////////////////////////
////////////////////////////////////////////////
// List
////////////////////////////////////////////////
Ownership<ElementType>* ownership_;
Tracing<ElementType>* tracing_;
public:
LengthCounter<ElementType, LengthType>* lengthCounter_;
List(Ownership<ElementType>* ownership,
Tracing<ElementType>* tracing,
LengthCounter<ElementType, LengthType>* lengthCounter,
ElementType&h, List *t=0):
ownership_(ownership), tracing_(tracing), lengthCounter_(lengthCounter)
{
setHead(h);
setTail(t);
}
~List()
{
ownership_->destroy(head_);
delete(ownership_);
delete(tracing_);
delete(lengthCounter_);
}
void setHead(ElementType& h)
{
tracing_->setHead(h);
head_ = ownership_->copy(h);
}
ElementType& head()
{
tracing_->head();
return *head_;
}
////////////////////////////////////////////////
// LengthCounter variations
////////////////////////////////////////////////
private:
LengthType length_;
public:
LengthType length()
{ return length_; }
Figura 5.47. Codificación de una flexibilización del ejemplar basada en la composición y la herencia de clases.
MyClass e;
Figura 5.48. Ejemplo de especificación de una lista para la flexibilización de la figura 5.42.
////////////////////////////////////////////////
// Ownership
////////////////////////////////////////////////
class OwnedReference {
public:
void destroy(ElementType* e) { delete e;}
ElementType* copy(ElementType& e) { return &e; }
};
////////////////////////////////////////////////
// Tracing
////////////////////////////////////////////////
void head() {
cout << "head()" << endl;
}
void setTail() {
cout << "setTail(t)" << endl;
}
void tail() {
cout << "tail()" << endl;
}
};
////////////////////////////////////////////////
// List
////////////////////////////////////////////////
Configuration::Ownership ownership_;
Configuration::Tracing tracing_;
public:
Configuration::LengthCounter lengthCounter_;
~List()
{
ownership_.destroy(head_);
}
void setHead(Configuration::ElementType& h)
144 Capítulo 5
{
tracing_.setHead(h);
head_ = ownership_.copy(h);
}
Configuration::ElementType& head()
{
tracing_.head();
return *head_;
}
List *tail()
{
tracing_.tail();
return tail_;
}
};
////////////////////////////////////////////////
// LengthCounter
////////////////////////////////////////////////
struct Configuration
{
typedef MyClass ElementType;
typedef Copy<ElementType> Ownership;
typedef WithLengthCounter<Configuration> LengthCounter;
typedef short LengthType;
typedef WithTracing<ElementType> Tracing;
};
MyClass e;
List<Configuration> list(e);
Figura 5.50. Ejemplo de especificación de una lista para la flexibilización de la figura 5.49.
Figura 5.51. Diseño de una flexibilización del ejemplar basada en herencia múltiple.
3. Modificar el destructor ~List, para que cuando se destruya una lista de tipo
ExternalReference u OwnedReference, la invocación automática a ~List 59, no libere los
elementos de la lista.
b) Los posibles “puntos de enganche” al ejemplar son los nombres de sus métodos y
atributos. El grosor de estos puntos es excesivo e introduce acoplamientos artificiales
entre el código variable que el compilador no podrá resolver. Por ejemplo, los métodos
setTail de las clases LengthCounterList y TracingList se enganchan al método setTail de List
para modificar el comportamiento de éste último. Aunque conceptualmente
LengthCounterList y TracingList son independientes, y las variaciones que producen sobre
List también lo son, un compilador será incapaz deducir cómo la clase MyList debe
heredar el método setTail.
59 “Automatic destructor calls: although you are often required to make explicit constructor calls, you never need to make
explicit destructor calls because there’s only one destructor for any class, and it doesn’t take any arguments. The compiler ensures
that all destructors are called, and that means all of the destructors in the entire hierarchy, starting with the most-derived
destructor and working back to the root.” [Eck00, capítulo 14]
Estudio comparativo de metodologías y técnicas de desarrollo de familias de productos 147
Figura 5.53. Diseño de una flexibilización del ejemplar basada en herencia simple.
Por brevedad, la figura 5.54 sólo incluye el código referente a una lista con
pertenencia de tipo Copy, de la que puede conocerse su longitud, y capaz de imprimir
mensajes de depuración (clases azules de la figura 5.53). El resto del código puede
consultarse en el CDROM anexo a esta tesis.
public:
List(ElementType&h, List<ElementType> *t=0)
{
setHead(h);
setTail(t);
}
return *head_;
}
////////////////////////////////////////////////
// Ownership
////////////////////////////////////////////////
~CopyList()
{ delete(head_); }
////////////////////////////////////////////////
// LengthCounter
////////////////////////////////////////////////
LengthType length()
{ return length_; }
private:
LengthType length_;
LengthType computedLength()
{
if (tail())
return static_cast<LenCopyList<ElementType, LengthType>*>(tail())->length()+1;
else
return 1;
}
};
////////////////////////////////////////////////
// Tracing
////////////////////////////////////////////////
void setHead(ElementType& h)
{
cout << "setHead(" << h << ")" << endl;
LenCopyList<ElementType, LengthType>::setHead(h);
}
ElementType& head()
{
cout << "head()" << endl;
return LenCopyList<ElementType, LengthType>::head();
}
Figura 5.54. Extracto de la codificación de una generalización del ejemplar basada en herencia simple.
Figura 5.55. Diseño de una flexibilización del ejemplar basada en herencia parametrizada.
Estudio comparativo de metodologías y técnicas de desarrollo de familias de productos 151
////////////////////////////////////////////////
// List
////////////////////////////////////////////////
public:
List(ElementType&h, List<ElementType> *t=0)
{
setHead(h);
setTail(t);
}
////////////////////////////////////////////////
// Ownership
////////////////////////////////////////////////
public:
ExtRefList(ElementType&h, ExtRefList<ElementType> *t=0): List<ElementType>(h, t) {}
public:
OwnRefList(ElementType&h, OwnRefList<ElementType> *t=0): List<ElementType>(h, t) {}
~OwnRefList()
{ delete(head_); }
public:
CopyList(ElementType&h, CopyList<ElementType> *t=0): List<ElementType>(h, t) {}
~CopyList()
{ delete(head_); }
////////////////////////////////////////////////
// LengthCounter
////////////////////////////////////////////////
public:
LengthList(ElementType_&h, LengthList<BaseList, LengthType> *t=0):
BaseList(h, t)
{
length_ = computedLength();
}
LengthType length()
{ return length_; }
private:
LengthType length_;
LengthType computedLength()
{
if (tail())
return static_cast<LengthList<BaseList, LengthType>*>(tail())->length()+1;
else
return 1;
}
};
////////////////////////////////////////////////
// Tracing
////////////////////////////////////////////////
public:
TracingList(ElementType_&h, TracingList<BaseList> *t=0): BaseList(h, t) {}
void setHead(ElementType_& h)
{
cout << "setHead(" << h << ")" << endl;
BaseList::setHead(h);
}
ElementType_& head()
{
cout << "head()" << endl;
return BaseList::head();
}
{
cout << "setTail(t)" << endl;
BaseList::setTail(t);
}
};
Funcionalidad fija de todas las listas ElementType Ownership LengthCounter Tracing
Figura 5.56. Codificación de una generalización del ejemplar basada en herencia parametrizada.
Para obtener, por ejemplo, una lista con pertenencia de tipo Copy, de la que puede
conocerse su longitud y capaz de imprimir mensajes de depuración, bastaría con escribir la
siguiente sentencia:
En este caso, los puntos de enganche serían los join points que pueden seleccionarse
con un pointcut. Afortunadamente, el grosor de los join points es suficientemente fino
(llamadas a métodos, accesos a atributos, inicialización de objetos…) como para evitar los
acoplamientos entre el código variable que introduce la herencia.
Sin embargo, las extensiones de orientación a aspectos para GPLs suelen sufrir
limitaciones que impiden flexibilizaciones realmente no invasivas del ejemplar.
Concretamente, AspectC++ sólo permite los siguientes cambios estructurales
(denominados static crosscutting): la adición de atributos y métodos a una clase, y la alteración
del padre de una clase. Es decir, los mismos cambios que la herencia parametrizada. Por lo
tanto, las modificaciones señaladas en la figura 5.52 no podrían realizarse60.
60Si el ejemplar estuviera escrito en Java, con AspectJ tampoco podríamos especificar en un aspecto los
cambios citados [AJPG06].
154 Capítulo 5
[Zha05]. Los aspectos son paquetes con el estereotipo <<aspect>>, que pueden contener
otros aspectos o un pointcut y su advice asociado, los cuales se representan como paquetes
con los estereotipos <<pointcut>> y <<advice>>. La clase List sería el ejemplar
retocado, que implementaría exclusivamente los requisitos fijos. Añadiéndole los aspectos
[ExternalReference|OwnedReference|Copy][LengthCounter][Tracing]
se modificaría su comportamiento para adaptarlo a la especificación de cualquier producto.
Lamentablemente, esta flexibilización seguiría siendo irrealizable porque en AspectC++ los
aspectos no pueden ser genéricos ni actuar sobre clases genéricas61, 62 y la implementación
de los tipos variables ElementType y LengthCounterType requeriría que los aspectos
ExternalReference, OwnedReference, Copy, LengthCounter y Tracing fueran plantillas y que, además,
los aspectos actuaran sobre otra plantilla, la clase List.
61 “Currently ac++ is able to parse a lot of the (really highly complicated) C++ templates, but weaving is restricted to non-
templated code only. That means you can not weave in templates or even affect calls to template functions or members of template
classes.” [Spi06, página 19]
62 Aunque la versión 1.5.2 de AspectJ permite el desarrollo de aspectos genéricos y la adición de aspectos
sobre clases genéricas, la propia genericidad de Java posee importantes limitaciones: “All instances of a generic
class have the same run-time class, regardless of their actual type parameters […] As consequence, the static variables and
methods of a class are also shared among all the instances. That is why it is illegal to refer to the type parameters of a type
declaration in a static method or initializer, or in the declaration or initializer of a static variable.” [Bra04, página 14]
Estudio comparativo de metodologías y técnicas de desarrollo de familias de productos 155
Figura 5.57. Diseño, según la notación propuesta en [Zha05], de una flexibilización del ejemplar basada en
orientación a aspectos.
~<%=@list_specificacion['Element Type']%>List()
{
<% if @list_specificacion['Ownership'] == 'Owned reference' or
@list_specificacion['Ownership'] == 'Copy'%>
delete head_;
<% end %>
}
Código Metacódigo
Funcionalidad fija de todas las listas ElementType Ownership LengthCounter Tracing
require "erb/erb"
unless ARGV[0]
print "You should specify the list specification file"
exit
end
######################################
# analyzer
######################################
######################################
# generator call
######################################
erb = ERB.new(File.open("TemplateList.cpp").read)
list_code = erb.result(binding)
File.open(@list_specificacion['Out File'], "w").write(list_code)
Figura 5.59. Análisis de especificaciones DSL y generación de listas con plantillas ERB.
Las plantillas de código son la técnica externa análoga a las sentencias de selección
(sección 5.2.3.1). Como aquel tipo de flexibilización, produce líneas de productos
monolíticas y con excesivo acoplamiento. Estos inconvenientes pueden atenuarse
extrayendo de la plantilla el código variable y modularizándolo con subprogramas y clases,
de manera similar a como se planteó en las secciones 5.2.3.2-5.2.3.4.
5.3. Conclusiones
En la sección 5.1 se ha planteado un ejemplo que resalta dos factores clave de las familias
de productos: el nivel de ocultación de la infraestructura que facilita la obtención
automática de los productos y la eficiencia de los productos finales.
La metodología EDD, original de esta tesis, propone una estrategia innovadora que
trata de sacar partido a la similitud entre los productos de una familia afrontando su
construcción por analogía respecto a un ejemplar de la familia realizado anteriormente. Así,
el desarrollo se descompone en dos grandes etapas: la construcción de un ejemplar y su
posterior flexibilización. Mientras que la primera etapa aprovecha la madurez de la
ingeniería del software clásica para implementar los requisitos fijos del dominio, presentes
en cualquier producto, la segunda busca cómo derivar automáticamente del ejemplar los
demás productos de la familia.
En las secciones 5.1.2.3 y 5.2.2 se han implementado con ETL dos flexibilizaciones
satisfactorias, que gozan de las siguientes cualidades:
6
Ejemplos de aplicación
I would rather write programs to help me write programs than write programs.
El presente capítulo incluye cinco ejemplos que ilustran la potencia y versatilidad de EDD
y ETL.
Por último, el quinto ejemplo muestra cómo extender la sintaxis de Java para que
este lenguaje sea más conciso.
162 Capítulo 6
Como alternativa, el código puede documentarse por separado. Sin embargo, como
señalan los defensores de los procesos ágiles, la documentación “en papel” suele ser
costosa y propensa a contener incongruencias con el código, ya que la evolución del código
no siempre se refleja en el papel [Bec02, página 159].
Desde que Java adoptara esta solución, proporcionando la herramienta Javadoc [Jav06]
muchos lenguajes han incorporado herramientas análogas, como Rdoc para Ruby [Rdo07],
POD para PERL [POD06]…
Ejemplos de aplicación 163
- la tabla.
<frameset cols=”20%,*”>
<frame src=”tables.html”>
<frame name=”main” src=”tables.html”>
</frameset>
<html>
<body>
</body>
</html>
<html><head>
<title>Book</title>
</head>
<body>
<table width=100%>
<tr>
<td width=10% nowrap valign=top>6.2Table Name:</td><td>Book</td></tr>
<td width=10% nowrap valign=top>6.2Description:</td><td>Book contains information about each
book, the title, the author, the publisher, etc.</td></tr>
<td width=10% nowrap valign=top>6.2Fields:</td><td>
<table cellspacing=1 cellpadding=0 bgcolor=#bbbbbb width=100%>
<tr>
<td width=20% nowrap valign=top>6.2Name</b></td>
<td width=20% nowrap valign=top>6.2Type</b></td>
<td width=20% nowrap valign=top>6.2Constraints</b></td>
<td width=40% valign=top>6.2Comments</b></td>
</tr>
<tr>
<td bgcolor=white valign=top>bookID</td>
<td bgcolor=white valign=top>integer</td>
<td bgcolor=white valign=top>Non-null</td>
<td bgcolor=white valign=top>The primary key id</td>
</tr><tr>
<td bgcolor=white valign=top>title</td>
<td bgcolor=white valign=top>varchar(80)</td>
<td bgcolor=white valign=top>Non-null</td>
<td bgcolor=white valign=top>The title of the book</td>
</tr><tr>
Ejemplos de aplicación 165
Figura 6.6. Sintaxis abstracta de un DSL para documentar la creación de tablas en SQL.
Figura 6.7. Sintaxis concreta de un DSL para documentar la creación de tablas en SQL.
64 Como se aprecia en la figura 6.11, SQLDoc generará para el ejemplo de la figura 6.8 los ficheros index.html,
tables.html, Cliente.html, Impositor.html, Cuenta.html y Sucursal.html.
Ejemplos de aplicación 167
<html>
<body>
</body>
</html>
Figura 6.9. Generación obtenida por SQLDoc a partir de la figura 6.8 (Tables.html).
<html><head>
<title>Cuenta</title>
</head>
<body>
<table width=100%>
<tr>
<td width=10% nowrap valign=top>6.2Table Name:</td><td>Cuenta</td></tr>
<td width=10% nowrap valign=top>6.2Description:</td><td>Cuentas bancarias</td></tr>
<td width=10% nowrap valign=top>6.2Fields:</td><td>
<table cellspacing=1 cellpadding=0 bgcolor=#bbbbbb width=100%>
<tr>
<td width=20% nowrap valign=top>6.2Name</b></td>
<td width=20% nowrap valign=top>6.2Type</b></td>
<td width=20% nowrap valign=top>6.2Constraints</b></td>
<td width=40% valign=top>6.2Comments</b></td>
</tr>
<tr>
<td bgcolor=white valign=top>numero</td>
<td bgcolor=white valign=top>integer</td>
<td bgcolor=white valign=top>Non-null</td>
<td bgcolor=white valign=top>Identificador de una cuenta (clave primaria)</td>
</tr><tr>
<td bgcolor=white valign=top>nombre</td>
<td bgcolor=white valign=top>varchar(80)</td>
<td bgcolor=white valign=top>Non-null</td>
<td bgcolor=white valign=top>Nombre de la sucursal a la que está asociada la cuenta</td>
</tr><tr>
<td bgcolor=white valign=top>saldo</td>
<td bgcolor=white valign=top>integer</td>
<td bgcolor=white valign=top>Non-null</td>
<td bgcolor=white valign=top>Saldo disponible en la cuenta</td>
</tr></table>
</td></tr>
<td width=10% nowrap valign=top>6.2Relates To:</td>
<td valign=top><a href="Sucursal.html">Sucursal</a></td>
</tr></table>
</body>
</html>
Figura 6.10. Generación obtenida por SQLDoc a partir de la figura 6.8 (Cuenta.html).
168 Capítulo 6
Finalmente, las figuras 6.12 y 6.13 son respectivamente el diseño y la codificación del
generador. Se han utilizado los colores , , , , y para identificar el código
variable en el ejemplar (figuras 6.4 y 6.5), las sustituciones que lo adaptarán (figura 6.13) y el
código objeto que producirán (figuras 6.9 y 6.10).
#################################################
# Analyzer
#################################################
require "analyzer/Analyzer"
unless ARGV[0]
print "SQLDoc usage: sqldoc sql_file\n"
exit
end
fh = File.open(ARGV[0])
in_text = fh.read()
fh.close()
analyzer = Analyzer.new(in_text)
#################################################
# Generator
#################################################
require '..\..\ETL\ETL'
produceTableIndex
produceEachTable
prod("#{@exemplar}/index.html", "#{@output}/index.html", [], 'produceIndex')
end
170 Capítulo 6
def produceTableIndex
tableRefRegExp = /((<a href=")\w+(.html" target="main">)\w+(<\/a><br>\s*))+/
tableRefMatch = extract("#{@exemplar}/tables.html", tableRefRegExp)
tableRefSub = ''
@tables.each {|table|
tableRefSub += tableRefMatch[2] + table.name +
tableRefMatch[3] + table.name + tableRefMatch[4]
}
sub(tableRefRegExp, tableRefSub, 'tables')
prod("#{@exemplar}/tables.html", "#{@output}/tables.html", ['tables'])
end
def produceEachTable
@tables.each { |table|
comment_analyzer = @analyzer.comment_analyzer(table.comment)
table.fields.each { |field|
field.comment = comment_analyzer.fieldDescriptions[ field.name.to_s.downcase.strip ]
}
table_comment = comment_analyzer.tableDescription
relates_to = comment_analyzer.relatesTo
sub(/(<title>).+?(<\/title>)/, '\1'+table.name+'\2',
"tableTitle_#{table.name}")
sub(/(Table Name:<\/td><td>).+?(<)/, '\1'+table.name+'\2',
"tableName_#{table.name}")
sub(/(Description:<\/td><td>).+?(<)/, '\1'+table_comment+'\2',
"tableDescription_#{table.name}")
fieldsRegExp = /<tr>\s*((<td\s+bgcolor.+<\/td>\s*){4,}<\/tr>(<tr>)?\s*)+/
fieldsMatch = extract("#{@exemplar}/Book.html", fieldsRegExp)
fieldMatch = /(<tr>\s*)((<td.+?>).+?(<\/td>\s*))*(<\/tr>)/.match(fieldsMatch[0])
fieldsSub = ''
table.fields.each { |field|
constraints = []
constraints.push( "Non-null" ) if ( field.not_null )
constraints.push( "Unique" ) if ( field.unique )
constraints.push( "Primary key" ) if ( field.primary_key )
fieldsSub += fieldMatch[1] +
fieldMatch[3] + field.name.to_s + fieldMatch[4] +
fieldMatch[3] + field.type + fieldMatch[4] +
fieldMatch[3] + constraints.join(", ") + fieldMatch[4] +
fieldMatch[3] + field.comment + fieldMatch[4] +
fieldMatch[5]
}
sub(fieldsRegExp, fieldsSub, "field_#{table.name}")
prod("#{@exemplar}/Book.html", "#{@output}/#{table.name}.html",
["tableTitle_#{table.name}",
"tableName_#{table.name}",
"tableDescription_#{table.name}",
"field_#{table.name}",
"relation_#{table.name}"])
}
end
end
"Pedro", 30
"Juan Antonio", 35
"Carmen", 29
import java.io.*;
import java.util.*;
/**
* This is the base class for the CSV reader. You should derive your class from this
* and make any modifications you like.
*/
public class PeopleReader {
/**
* The data structure class
*/
public class People
{
/**
* The Name
*/
public String name;
/**
* The Age
*/
public Integer age;
}
/**
* size returns the count of data rows found in the input.
*/
public int size()
{
return _data.size();
}
/**
* Returns the 'People' object for the specified row.
*/
public People get( int index )
{
return (People) _data.get( index );
}
/**
172 Capítulo 6
try {
while( true )
{
int nChar;
if ( ( nChar = _in.read() ) == -1 ) {
if ( line.length() > 0 )
process_line( line );
break;
}
char cChar = (char)nChar;
if ( cChar == '\n' )
{
process_line( line );
line.setLength( 0 );
}
else
{
line.append( cChar );
} // if
} // while
} // try
catch( IOException except )
{
if ( line.length() > 0 )
process_line( line );
} // catch
} // read
/**
* Processes a single input line.
*
* @arg line The row text
*/
private void process_line( StringBuffer line )
{
ArrayList fields = new ArrayList();
if ( text.length() > 0 )
fields.add( new String( text ).trim() );
process_fields( strArray );
}
/**
* This is the main processor for a single row of string fields
*
* @arg fields The array of strings that make up the row.
*/
private void process_fields( String fields[] )
{
People data = new People();
data.name = fields[0];
data.age = Integer.getInteger( fields[1] );
_data.add( data );
}
Como muestra la figura 6.15, la lectura de ficheros de datos no es trivial. Por esta
razón, y dado que es una tarea muy común, se han desarrollado soluciones generales como
los meta-analizadores lex y yacc [BLM92] o los analizadores DOM (Document Object Model) y
SAX (Simple API for XML) de XML [Har02].
En la presente sección se aplicará EDD y ETL para realizar, utilizando la figura 6.15
como ejemplar, una infraestructura de desarrollo automático de lectores de ficheros CSV.
Figura 6.17. Documentación, generada con la herramienta javadoc, del lector de personas.
Ejemplos de aplicación 175
import junit.framework.*;
import java.io.*;
try
{
in = new FileInputStream( "data.csv" );
in.close();
} catch( Exception e ) {
System.err.println(e);
} // catch
} // setUp
Figura 6.19. Gramática EBNF del DSL con el que se especificarán los lectores de ficheros CSV.
<spec classname="Book">
<field name="title" type="String" comment="The title" />
<field name="author" type="String" comment="The author" />
<field name="size" type="Integer" comment="The number of pages" />
<field name="price" type="Float" comment="The price in dollars" />
</spec>
"Programming Ruby: The Pragmatic Programmers' Guide", "Dave Thomas, Andy Hunt", 564, 31.40
"The Ruby Way", "Hal Fulton", 600, 27.99
Figura 6.21. Ejemplo de fichero de datos para el lector especificado en la figura 6.20.
La figura 6.23 es el analizador que procesa las especificaciones XML de los lectores
aprovechando la librería REXML [Rus06]. El resto del análisis se resolverá mediante
expresiones regulares.
Ejemplos de aplicación 177
READER = 'PeopleReader.java'
TEST = 'PeopleTest.java'
xml_csv_grammar = ARGV[0]
csv_example = ARGV[1]
out_dir = ARGV[2]
read_csv_grammar(xml_csv_grammar, csv_grammar)
Figura 6.24. Ejemplo de generación del programa objeto especificado en la figura 6.20.
dataStructureClassRegExp = /
public\s+class\s+#{csv_grammar.classname}\s+\{
.*?
\}
/xm
sub(dataStructureClassRegExp, dataStructureClassTemplate(csv_grammar))
sub(/(\s*\bdata\..+?;)+/, process_fields_template(csv_grammar.fields))
prod("#{out_dir}/#{csv_grammar.classname}Reader.java",
"#{out_dir}/#{csv_grammar.classname}Reader.java")
end
def dataStructureClassTemplate(csv_grammar)
fieldsCode = ''
csv_grammar.fields.each { |field|
fieldsCode += dataStructureFieldTempate(field)
}
code = <<-END_OF_CODE
public class #{csv_grammar.classname}
{
#{fieldsCode}
}
END_OF_CODE
end
def dataStructureFieldTempate(field)
code = <<-END_OF_CODE
/**
* #{field.comment}
*/
public #{field.type} #{field.name};
END_OF_CODE
end
def process_fields_template(fields)
code = ''
fields.each_index { |i|
rigth_side = proccessField(fields[i].type, "fields[#{i}]")
code += "data.#{fields[i].name} = #{rigth_side};\n"
}
return code
end
end
gsub(/People/, csv_grammar.classname)
testsRegExp = /
(
public\s+\w+\s+test.+?\{
.*?
\}\s*\/\/\s*\w+\s*
)+
/xm
testsSub = ''
i = 1
File.open(csv_example).each_line{ |line|
csvRegExp = /
(?:^|,)\s*
(
"[^"]*"
|
[^",]*
180 Capítulo 6
)
/x
testData = line.scan(csvRegExp).flatten
testData.collect {|data|
data.tr!('"', '')
data.strip!
}
testsSub += testTemplate(i, testData, csv_grammar)
i += 1
}
sub(testsRegExp, testsSub)
prod(test, "#{out_dir}/#{csv_grammar.classname}Test.java")
end
module CSV_Utilities
end
(
ReaderGen.new(csv_grammar, READER, out_dir) +
TestGen.new(csv_grammar, csv_example, TEST, out_dir)
).gen
Figura 6.30. Tabla de seguimiento de la copia de Figura 6.31. Procedimientos almacenados para la
datos. copia de los datos contenidos en la tabla
TbCompProp.
Las figuras 6.32, 6.33, 6.34 y 6.35 son respectivamente los códigos de los
procedimientos almacenados sp_AprovTbCompProp, sp_ActuTbDiaCtlPrc, sp_ActuTbCompProp
y sp_ExisTbCompProp (los procedimientos están escritos en el lenguaje TRANSACT SQL ,
puesto que la organización utiliza el gestor de base de datos Microsoft SQL Server).
/* sp_AprovTbCompProp */
/*----------------------*/
GO
@P_CodUsu int ,
@P_Actualizacion int = 1 ,
@P_Validacion int = 0 ,
@P_Referencial int = 0 ,
@P_Transformacion int = 1 ,
@P_Mensajes int = 2 ,
@P_NumRegLOG int = -1 ,
@P_NumRegDiaCtlPrc int = 5000 ,
@P_Error int OUTPUT
AS
/* */
/** DECLARACION DE VARIABLES LOCALES DEL PROCEDIMIENTO. **/
/* */
/** Registros locales para tratar la tabla BASE. **/
SET NOCOUNT ON
SELECT @P_Error = 0
SELECT @L_ErrorActu = 0
SELECT @msg = ''
SELECT @msg_Log = ''
SELECT @L_NumFilasTotal = 0
SELECT @L_NumFilasTratadas = 0
SELECT @L_NumFilasError = 0
SELECT @L_NumFilasActu = 0
SELECT @L_NumFilasAlta = 0
SELECT @L_NumFilasModif = 0
IF @P_Mensajes > 0
BEGIN
SELECT 'Inicio' = CONVERT(varchar, @L_Inicio ,113)
PRINT ''
END
/* */
/** Acceso a la tabla TEMPORAL de datos. **/
SELECT
CodProp ,
CodPropComp ,
CodUsu ,
FecAlta ,
FecMod ,
FecBaja ,
NumOrdProp
FROM TemTbCompProp
ORDER BY
CodProp ,
CodPropComp
IF @@ERROR <> 0
184 Capítulo 6
BEGIN
SELECT @msg = 'ERROR de acceso a tabla TEMPORAL.'
GOTO Err_Aprov
END
OPEN rs
IF @@ERROR <> 0
BEGIN
SELECT @msg = 'ERROR de apertura del CURSOR.'
GOTO Err_Aprov
END
SELECT @L_CursorAbierto = 1
IF @P_Mensajes > 0
BEGIN
SELECT 'Tiempo Apertura Cursor' = DateDiff(ms, @L_Inicio ,GetDate()) , 'Num.Filas.Cursor'
= @L_NumFilasTotal
PRINT ''
END
IF @@ERROR <> 0
BEGIN
SELECT @msg = 'ERROR al recuperar informacion del CURSOR.'
GOTO Err_Aprov
END
SELECT @L_NumEvt = 0
EXEC sp_ActuTbDiaCtlPrc
@P_CodPrc = 'sp_AprovTbCompProp' ,
@P_CodUsu = @P_CodUsu ,
@P_CodEvt = 'INICIO' ,
@P_NumEvt = @L_NumEvt OUTPUT ,
@P_IdObjEvt = @msg_Log
BEGIN
COMMIT
IF @@ERROR <> 0
BEGIN
SELECT @msg = 'ERROR al truncar el LOG de transacciones.'
GOTO Err_Aprov
END
IF @P_Mensajes = 1
BEGIN
Ejemplos de aplicación 185
BEGIN TRANSACTION
EXEC sp_ActuTbDiaCtlPrc
@P_CodPrc = 'sp_AprovTbCompProp' ,
@P_CodUsu = @P_CodUsu ,
@P_CodEvt = '' ,
@P_NumEvt = @L_NumEvt ,
@P_IdObjEvt = @msg_Log
IF @P_Mensajes > 0
BEGIN
SELECT
'TOTAL Filas' = @L_NumFilasTotal ,
'Filas TRATADAS' = @L_NumFilasTratadas ,
'Tiempo' = DateDiff(ms, @L_UltimaVez ,GetDate()) ,
'Tiempo Total' = DateDiff(ms, @L_Inicio ,GetDate()) ,
'Filas Actu' = @L_NumFilasActu ,
'Filas Alta' = @L_NumFilasAlta ,
'Filas Modif' = @L_NumFilasModif ,
'Filas Error' = @L_NumFilasError
PRINT ''
END
SELECT @L_RegAlta = 0
SELECT @L_RegModif = 0
SELECT @L_ErrorActu = 0
EXEC sp_ActuTbCompProp
@L_CodProp ,
@L_CodPropComp ,
@P_CodUsu ,
@L_FecAlta ,
@L_FecMod ,
@L_FecBaja ,
@L_NumOrdProp ,
@P_Mensajes = @P_Mensajes ,
@P_Actualizacion = @P_Actualizacion ,
@P_Validacion = @P_Validacion ,
@P_Referencial = @P_Referencial ,
@P_Transformacion = @P_Transformacion ,
@P_RegCreado = @L_RegAlta OUTPUT ,
@P_RegModificado = @L_RegModif OUTPUT ,
@P_Error = @L_ErrorActu OUTPUT
SELECT @L_UltClaveActu =
@L_CodProp + '' +
@L_CodPropComp
186 Capítulo 6
IF @L_RegAlta = 1
SELECT @L_NumFilasAlta = @L_NumFilasAlta + 1
IF @L_RegModif = 1
SELECT @L_NumFilasModif = @L_NumFilasModif + 1
IF (@L_RegAlta = 1) OR (@L_RegModif = 1)
SELECT @L_NumFilasActu = @L_NumFilasActu + 1
IF @@ERROR <> 0
BEGIN
SELECT @msg = 'ERROR en recuperacion del CURSOR.'
GOTO Err_Aprov
END
CLOSE rs
DEALLOCATE rs
SELECT @L_CursorAbierto = 0
IF @P_Mensajes = 1
BEGIN
PRINT 'FIN del tratamiento de registros.'
PRINT ''
END
/* */
/** FIN DEL PROCEDIMIENTO. **/
EXEC sp_ActuTbDiaCtlPrc
@P_CodPrc = 'sp_AprovTbCompProp' ,
@P_CodUsu = @P_CodUsu ,
@P_CodEvt = 'FIN' ,
@P_NumEvt = @L_NumEvt ,
@P_IdObjEvt = @msg_Log
IF @P_Mensajes > 0
BEGIN
SELECT
'TOTAL Filas' = @L_NumFilasTotal ,
'Filas TRATADAS' = @L_NumFilasTratadas ,
'Tiempo' = DateDiff(ms, @L_UltimaVez ,GetDate()) ,
'Tiempo Total' = DateDiff(ms, @L_Inicio ,GetDate()) ,
'Filas Actu' = @L_NumFilasActu ,
'Filas Alta' = @L_NumFilasAlta ,
'Filas Modif' = @L_NumFilasModif ,
Ejemplos de aplicación 187
RETURN
/* */
/** Tratamiento de ERRORES. **/
Err_Aprov:
SELECT @P_Error = 1
IF @L_CursorAbierto = 1 DEALLOCATE rs
EXEC sp_ActuTbDiaCtlPrc
@P_CodPrc = 'sp_AprovTbCompProp' ,
@P_CodUsu = @P_CodUsu ,
@P_CodEvt = 'FIN' ,
@P_NumEvt = @L_NumEvt ,
@P_IdObjEvt = @msg_Log
RAISERROR (@msg,16,1)
RETURN
GO
/* sp_ActuTbDiaCtlPrc */
/*----------------------*/
GO
@P_CodPrc char(30) ,
@P_CodUsu int ,
@P_CodEvt char(30) = '' ,
@P_NumEvt int = 0 OUTPUT ,
@P_IdObjEvt varchar(255) = '' ,
@P_DesObjEvt text = '' ,
@P_ExprObjEvt text = ''
AS
/* */
/** INICIO DEL PROCEDIMIENTO. **/
/* */
/** Calculo del numero siguiente de evento para el proceso. **/
IF @P_NumEvt = 0
188 Capítulo 6
BEGIN
SELECT @P_NumEvt = MAX(NumEvt)
FROM TbDiaCtlPrc
WHERE CodPrc = @P_CodPrc
IF @P_NumEvt IS NULL
SELECT @P_NumEvt = 1
ELSE
SELECT @P_NumEvt = @P_NumEvt + 1
END
/* */
/** Actualizacion del Diario de Control de Procesos. **/
VALUES (
GetDate() ,
@P_CodPrc ,
@P_CodUsu ,
@P_CodEvt ,
@P_NumEvt ,
@P_IdObjEvt ,
@P_DesObjEvt ,
@P_ExprObjEvt )
/* */
/** FIN DEL PROCEDIMIENTO. **/
GO
/* sp_ActuTbCompProp */
/*---------------------*/
GO
@P_CodUsu int ,
@P_FecAlta char (8) ,
@P_FecMod char (8) ,
@P_FecBaja char (8) ,
@P_NumOrdProp int ,
/* */
/** Otros parametros del procedimiento. **/
@P_ExisteRegEnBase int = -1 ,
@P_Mensajes int = 2 ,
Ejemplos de aplicación 189
@P_Actualizacion int = 1 ,
@P_Validacion int = 0 ,
@P_Referencial int = 1 ,
@P_Transformacion int = 1 ,
@P_RegCreado int OUTPUT,
@P_RegModificado int OUTPUT,
@P_Error int OUTPUT
AS
/* */
/** DECLARACION DE VARIABLES LOCALES DEL PROCEDIMIENTO. **/
SELECT @P_Error = 0
SELECT @L_ErrorExistencia = 0
SELECT @L_ErrorTransformacion = 0
SELECT @P_RegCreado = 0
SELECT @P_RegModificado = 0
IF @P_ExisteRegEnBase = -1
BEGIN
END
/* */
/** Actualizacion de la estructura de datos. **/
IF @P_Actualizacion = 1
BEGIN
IF @P_ExisteRegEnBase = 0
BEGIN
VALUES (
@P_CodProp ,
@P_CodPropComp ,
@P_CodUsu ,
@L_FecAlta ,
@L_FecMod ,
@L_FecBaja ,
@P_NumOrdProp )
IF @P_Error <> 0
BEGIN
SELECT @msg = 'ERROR de ALTA en TbCompProp .'
GOTO Err_Act
END
ELSE /* ExisteRegEnBase = 1. */
BEGIN
UPDATE TbCompProp
SET
CodUsu = @P_CodUsu ,
FecMod = @L_FecMod ,
NumOrdProp = @P_NumOrdProp
WHERE
CodProp = @P_CodProp AND
CodPropComp = @P_CodPropComp
IF @P_Error <> 0
BEGIN
SELECT @msg = 'ERROR de MODIFICACION en TbCompProp .'
GOTO Err_Act
END
RETURN
/* */
/** Tratamiento de ERRORES. **/
Err_Act:
SELECT @P_Error = 1
PRINT @msg
RETURN
GO
/* sp_ExisTbCompProp */
/*---------------------*/
GO
/* */
/** - Declaracion de identificadores de estructura_composicion_propiedad. **/
AS
/* */
/** 3.- Inicio del procedimiento. **/
SELECT @P_Error = 0
/* */
/** - Si existe elemento en estructura_composicion_propiedad devuelve 1 (verdadero). **/
/** - Si no existe elemento en estructura_composicion_propiedad devuelve 0 (falso). **/
IF EXISTS (
SELECT
CodProp ,
CodPropComp
FROM TbCompProp
WHERE
CodProp = @P_CodProp AND
CodPropComp = @P_CodPropComp )
RETURN (1)
ELSE
RETURN (0)
/* */
/** 4.- Fin del procedimiento. **/
/* */
/** - Si error, activar variable local 'error'. **/
GO
65 E. Arellano muestra en su tesis doctoral [Are98] el ahorro que supone el uso de metadatos en la
especificación de sistemas de información.
Ejemplos de aplicación 193
########################################################
# Extracción de la especificación de la Base de Datos
########################################################
require 'dbi'
dbh = DBI.connect("dbi:ODBC:BDatos")
tablas.each { |tabla|
# Obtención de las columnas de la tabla
sth = dbh.execute("
SELECT syscolumns.name, systypes.name, syscolumns.length
FROM sysobjects, syscolumns, systypes
WHERE sysobjects.name = '#{tabla}'
AND syscolumns.id = sysobjects.id
AND syscolumns.xtype = systypes.xtype
")
columnas = {}
while row = sth.fetch do
columnas[row[0]] = row[1] + "(#{row[2]})"
end
claves = []
while row = sth.fetch do
claves += [row[0]]
end
#########################################
# Ejecución del generador
#########################################
Figura 6.38. “Análisis” de la especificación de los procedimientos almacenados para las tablas TbAtrTb y TbTb.
Ejecución del generador AprovTbGen.
Figura 6.39. Ejemplo de generación de los procedimientos almacenados que copian los datos de la tabla TbAtrTb.
Ejemplos de aplicación 195
############################################################
# Eliminación del ancho en las columnas de tipo: int, text
############################################################
#########################################
# Sustituciones
#########################################
gsub(/TbCompProp/, nombreTabla,
'nombre de la tabla')
sub(/((DECLARE @L_).+$\n)+/,
'\2' + columnas.keys + ' ' + columnas.values,
'registros locales para tratar la tabla base'
)
gsub(/\bCodProp\s*,\s*\n(.*,\s*)+NumOrdProp/,
columnas.keys.join(", \n "),
'lista de las columnas de la tabla base unidas por comas'
)
sub(/(ORDER BY\s*).*,\n.*/,
'\1' + claves.join(", \n\t"),
'ORDER BY'
)
gsub(
/
@L_CodProp\s*,\s*
(@L_\w+\s*,\s*)+
@L_NumOrdProp\s*?,?\s*?
/x,
('@L_' + columnas.keys ).join(",\n"),
'lista de las columnas de la tabla base precedidas por @L_ unidas por comas'
)
sub(
/
@L_CodProp\s*,\s*
(@L_\w+\s*,\s*)+
(@P_CodUsu\s*,\s*)
(@L_\w+\s*,\s*)+
@L_NumOrdProp
/x,
columnas.keys.collect {|c|
if c == 'CodUsu'
"@P_#{c}"
else
"@L_#{c}"
end
}.join(",\n"),
'ejecución de sp_ActuTbCompProp'
)
sub(/@L_CodProp \s*\+\s*''\s*\+\s*@L_CodPropComp/,
('@L_' + claves).join(" + '' + "),
'SELECT @L_UltClaveActu'
)
196 Capítulo 6
texto = ''
columnas.each {|columna, tipo|
if claves.include? columna
texto += "@P_#{columna} #{tipo}, --Identificador de la estructura\n"
else
texto +="@P_#{columna} #{tipo}, --Descriptor de la estructura\n"
end
}
sub(
/
\/\*\* \s*Identificadores\s*de\s*la\s*estructura
.*
(\n\/\*\* \s*Otros\s*parametros\s*del\s*procedimiento)
/mx,
texto + '\1',
'declaracion de parametros del procedimiento sp_ActuTb'
)
sub(
/
@P_CodProp\s*,\s*
@P_CodPropComp\s*,
(?=\s*@L_ErrorExistencia)
/x,
'@P_' + claves + ",",
'ejecución de sp_ExisteTbCompProp'
)
sub(
/
@P_CodProp\s*,\s*
(@[PL]_\w+\s*,\s*)+
@P_NumOrdProp
/x,
columnas.keys.collect {|c|
if ['FecAlta', 'FecMod', 'FecBaja'].include? c
"@L_#{c}"
else
"@P_#{c}"
end
}.join(",\n"),
'VALUES de INSERT INTO TbCompProp'
)
sub(/
CodProp\s*=\s*@P_CodProp\s*AND\s*
CodPropComp\s*=\s*@P_CodPropComp
/x,
claves.collect {|c|
"#{c} = @P_#{c}"
}.join(" AND\n"),
'WHERE claves'
)
texto = ''
claves.each { |c|
texto += "@P_#{c} #{columnas[c]} ,\n"
Ejemplos de aplicación 197
}
sub(/
@P_CodProp\s*char\s*\(30\)\s*,\s*
@P_CodPropComp\s*char\s*\(30\)\s*,
/x,
texto,
'declaracion de parametros del procedimiento'
)
sub(/CodProp\s*,\s*CodPropComp/,
claves.join(",\n"),
'SELECT claves'
)
#########################################
# Producciones
#########################################
prod('Ejemplares/sp_AprovTbCompProp.sql',
"out/sp_Aprov#{nombreTabla}.sql",
['nombre de la tabla',
'registros locales para tratar la tabla base',
'lista de las columnas de la tabla base unidas por comas',
'ORDER BY',
'lista de las columnas de la tabla base precedidas por @L_ unidas por comas',
'ejecución de sp_ActuTbCompProp',
'SELECT @L_UltClaveActu'
]
)
prod('Ejemplares/sp_ActuTbCompProp.sql',
"out/sp_Actu#{nombreTabla}.sql",
['nombre de la tabla',
'declaracion de parametros del procedimiento sp_ActuTb',
'ejecución de sp_ExisteTbCompProp',
'lista de las columnas de la tabla base unidas por comas',
'VALUES de INSERT INTO TbCompProp',
'UPDATE TbCompProp',
'WHERE claves'
]
)
prod('Ejemplares/sp_ExisTbCompProp.sql',
"out/sp_ExisTb#{nombreTabla}.sql",
['nombre de la tabla',
'declaracion de parametros del procedimiento',
'SELECT claves',
'WHERE claves'
]
)
end
end
En esta sección se utiliza ETL para desarrollar la herramienta m2unit para las prueba
de unidades de programas escritos en Modula-2.
- Para permitir que los programas enriquecidos con pruebas sean traducibles a
código máquina por cualquier compilador de Modula-2, las pruebas se introducen
como comentarios (entre los símbolos (* y *)).
END Util.
(*
TEST Test1;
VAR
x: INTEGER;
BEGIN
Assert(Exp(2,2) = 4);
x := 8;
Assert(x = Exp(2,3));
END Test1;
*)
(*
TEST Test2;
VAR
x, y: INTEGER;
BEGIN
x := 1;
y := 2;
IntercambiarValores(x, y);
Assert(x = 2);
Assert(y = 1);
END Test2;
*)
END Util.
Figura 6.42. Módulo Util enriquecido con las pruebas Test1 y Test2.
==============================================
Running Test1 from module Util
----------------------------------------------
----------------------------------------------
Test OK
==============================================
==============================================
Running Test2 from module Util
----------------------------------------------
----------------------------------------------
Test OK
==============================================
MODULE Test1;
67 Cada módulo de entrada actúa simultáneamente como programa fuente y como ejemplar.
Ejemplos de aplicación 201
VAR
x: INTEGER;
VAR
TestOK: BOOLEAN; (* TRUE if the test is free of failures *)
assertNum: CARDINAL; (* assert's counter *)
BEGIN
TestOK := TRUE;
assertNum := 0;
WriteLn;
WriteString("==============================================");
WriteLn;
WriteString("Running Test1 from module Util");
WriteLn;
WriteString("----------------------------------------------");
WriteLn;
INC(assertNum);
IF NOT((Exp(2,2) = 4)) THEN
TestOK := FALSE;
WriteString(" - assert number ");
WriteCard(assertNum,0);
WriteString(" failed");
WriteLn;
END;
x := 8;
INC(assertNum);
IF NOT((x = Exp(2,3))) THEN
TestOK := FALSE;
WriteString(" - assert number ");
WriteCard(assertNum,0);
WriteString(" failed");
WriteLn;
END;
IF TestOK THEN
WriteString("----------------------------------------------");
WriteLn;
WriteString("Test OK");
WriteLn;
WriteString("==============================================");
WriteLn;
ELSE
WriteString("----------------------------------------------");
WriteLn;
WriteString("Test failed");
WriteLn;
WriteString("==============================================");
WriteLn;
END;
202 Capítulo 6
END Test1.
MODULE Test2;
VAR
x, y: INTEGER;
VAR
TestOK: BOOLEAN; (* TRUE if the test is free of failures *)
assertNum: CARDINAL; (* assert's counter *)
BEGIN
TestOK := TRUE;
assertNum := 0;
WriteLn;
WriteString("==============================================");
WriteLn;
WriteString("Running Test2 from module Util");
WriteLn;
WriteString("----------------------------------------------");
WriteLn;
x := 1;
y := 2;
IntercambiarValores(x, y);
INC(assertNum);
IF NOT((x = 2)) THEN
TestOK := FALSE;
WriteString(" - assert number ");
WriteCard(assertNum,0);
WriteString(" failed");
WriteLn;
END;
INC(assertNum);
IF NOT((y = 1)) THEN
TestOK := FALSE;
WriteString(" - assert number ");
WriteCard(assertNum,0);
WriteString(" failed");
WriteLn;
Ejemplos de aplicación 203
END;
IF TestOK THEN
WriteString("----------------------------------------------");
WriteLn;
WriteString("Test OK");
WriteLn;
WriteString("==============================================");
WriteLn;
ELSE
WriteString("----------------------------------------------");
WriteLn;
WriteString("Test failed");
WriteLn;
WriteString("==============================================");
WriteLn;
END;
END Test2.
require '..\..\ETL\ETL'
if @testCode == nil
raise Exception,
"on module #{@fileIn}, the test #{@testName} " +
"is empty or has some \nsyntactic errors"
end
moduleNameCode()
varCode()
mainCode()
prod(fileIn, "#{dirOut}\\#{testName}.mod")
end
def moduleNameCode()
sub(/((IMPLEMENTATION|DEFINITION)\s+)?(MODULE\s+)\w+(\s*;)/m,
'\3' + @testName + '\4')
sub(/(END\s+)\w+(\s*\.)/, '\1' + @testName + '\2')
end
def varCode()
localTestVarCodeRegExp = /
(\s*VAR\s+
.+?)
BEGIN
/xm
if @testCode[0] =~ localTestVarCodeRegExp
varCode = $1
else
varCode = ''
end
varCode += <<-END_OF_MODULA2_CODE
VAR
TestOK: BOOLEAN; (* TRUE if the test is free of failures *)
assertNum: CARDINAL; (* assert's counter *)
END_OF_MODULA2_CODE
204 Capítulo 6
beginRegExp = /
(?=
(
BEGIN
((?!(END\s+\w+;)).)+?
END\s+\w+\.
)
)
/xm
if extract(@fileIn, beginRegExp)
sub(beginRegExp, varCode)
else
sub(/(?=(END\s+\w+\s*\.))/m, varCode)
end
end
def mainCode()
insertBeginCode()
beginCode = <<-END_OF_MODULA2_CODE
TestOK := TRUE;
assertNum := 0;
WriteLn;
WriteString("==============================================");
WriteLn;
WriteString("Running #{@testName} from module #{@sourceModuleName}");
WriteLn;
WriteString("----------------------------------------------");
WriteLn;
END_OF_MODULA2_CODE
programCode = @testCode.to_s.scan(/BEGIN(.+?)END\s+#{@testName}\s*;/m).to_s
endCode = <<-END_OF_MODULA2_CODE
IF TestOK THEN
WriteString("----------------------------------------------");
WriteLn;
WriteString("Test OK");
WriteLn;
WriteString("==============================================");
WriteLn;
ELSE
WriteString("----------------------------------------------");
WriteLn;
WriteString("Test failed");
WriteLn;
WriteString("==============================================");
WriteLn;
END;
END_OF_MODULA2_CODE
sub(/(?=(END\s+\w+\s*\.))/, code)
end
def insertBeginCode()
beginRegExp = /
BEGIN
((?!(END\s+\w+;)).)+?
END\s+\w+\.
/xm
if !extract(@fileIn, beginRegExp)
sub(/(?=(END\s+\w+\s*\.))/m, "BEGIN\n")
end
end
end
.+?
END\s+\1\s*;
/mx
emptyCommentary = /\(\*\s*?\*\)/
gsub(testCommentary, '')
gsub(emptyCommentary, "\n")
prod(fileIn, fileOut)
end
end
importSub(method(:inOutTemplate), 'InOut')
importSub(method(:storageTemplate), 'Storage')
sub(/(MODULE.+?;)/m, '\1'+@importCode)
prod(fileIn, fileOut)
end
def inOutTemplate()
code = <<-END_OF_MODULA2_CODE
FROM InOut IMPORT
OpenInput, OpenOutput, RedirectInput, RedirectOutput, CloseInput, CloseOutput,
Read, ReadString, ReadLine, ReadInt, ReadCard, ReadWrd,
Write, WriteLn, WriteString, WriteLine, WriteInt, WriteCard, WriteOct, WriteHex,
WriteWrd, ReadLongInt, ReadLongCard, WriteLongInt, WriteLongCard;
END_OF_MODULA2_CODE
end
def storageTemplate()
code = <<-END_OF_MODULA2_CODE
FROM Storage IMPORT ALLOCATE, DEALLOCATE;
END_OF_MODULA2_CODE
end
end
def template(condition)
code = <<-END_OF_MODULA2_CODE
INC(assertNum);
IF NOT(#{condition}) THEN
TestOK := FALSE;
WriteString(" - assert number ");
WriteCard(assertNum,0);
WriteString(" failed");
WriteLn;
END;
END_OF_MODULA2_CODE
end
end
typeRegExp = /TYPE\s+((?!CONST)(?!TYPE).+?(?!:)=.+?;\s*)+/m
typeMatch = extract(defFile, typeRegExp)
code = ''
code += constMatch[0] if constMatch
code += typeMatch[0] if typeMatch
importRegExp = /((FROM|IMPORT).+?;\s*)+/m
sub(importRegExp, delay('#{$&}' + code))
prod(fileIn, fileOut)
end
end
# Main Program
AUTHOR_INFO = <<-END_AUTHOR_INFO
END_AUTHOR_INFO
TEST_DIR = 'Test'
unless ARGV[0]
print "m2unit usage: m2unit file_1 [file_2]...[file_n]\n"
exit
end
puts AUTHOR_INFO
`rmdir /s /q #{TEST_DIR}`
import java.math.*;
Figura 6.48. Programa Java enriquecido con la notación aritmética para la clase BigDecimal.
import java.math.*;
double d = 1.0;
double e = 2.0;
require '..\..\ETL\ETL'
require 'bigdeceqparser'
prod(fin, fout)
end
end
BigDecEqGen.new(ARGV[0], ARGV[1]).gen
class BigDecEqParser
prechigh
left '*' '/'
left '+' '-'
preclow
rule
end
def initialize(lexerClass)
@lexer_class = lexerClass
end
def parse(aString)
@lexer = eval "#{@lexer_class}.new(aString)"
do_parse
end
def next_token
@lexer.next_token
end
class Lexer
@@tmp = 0
def initialize(aString)
@str = aString
end
def next_token
token = nil
until token
if @str =~ /^[a-zA-Z_]([a-zA-Z_]|\d)*(\.[a-zA-Z_]([a-zA-Z_]|\d)*)*/
token_value = TokenValueClass.new($&, @@tmp, '')
token = [:ID, token_value]
@@tmp +=1
elsif @str =~ /^\s+/
# do nothing
elsif @str =~ /^(\+|-|\(|\)|\/|\*)/
token_value = TokenValueClass.new($&, @@tmp, '')
token = [$&, token_value]
@@tmp +=1
elsif @str =~ /^=/
token_value = TokenValueClass.new($&, @@tmp, '')
token = [$&, token_value]
elsif @str.length > 0
raise LexerError, "Invalid token at '#{@str[0,10]}'"
else
token = [false, '$end']
end # if
@str = $'
end # until
return token
end
end
7
Conclusiones y trabajo futuro
In every project a large portion of the work done by developers is not genuinely creative - it is
tedious routine work that we should try to automate [...] We may be able to get from 20/80
(20% creative content and 80% noncreative) to 80/20; I don't think we can eliminate it
entirely.
7.1. Conclusiones
En el capítulo 1 se ha señalado la importancia de desarrollar familias de productos en lugar
de productos aislados para conseguir una reutilización sistemática del software y lograr
economía de alcance.
- Los requisitos fijos de una familia de productos suelen ser más estables que los
requisitos variables. EDD separa la implementación de los requisitos fijos (el
Bibliografía 213
Sería deseable que una futura versión de EDD profundizara en los detalles y la
repercusión de este tipo de organización.
2. La mejora tecnológica de ETL. Para ello, podrían adoptarse las siguientes medidas:
Bibliografía
[ACHL02] Asencio, A.; Cardman, S.; Harris, D.; Laderman, E. Relating expectations to
automatically recovered design patterns. Ninth Working Conference on Reverse
Engineering. 29 Oct.-1 Nov. 2002 Page(s):87 – 96.
[AEHH96] Andries, M.; Engels, G; Habel, A.; Hoffmann, B.; Kreowski, H.; Kuske,
S.; Kuske, D.; Plump, D.; Schürr, A.; Taentzer, G. Graph Transformation
for Specification and Programming. Technical Report 7/96, Universität
Bremen, 1996, http://citeseer.nj.nec.com/article/andries96graph.html
[AISJ+77] Alexander, C.; Ishikawa, S.; Silverstein, M.; Jacobson, M.; Fiksdahl-King,
I.; Angel, S. A Pattern Language. Oxford University Press, Nueva York,
1977.
[BA99] Boehm, B.; Abts, C. COTS integration: plug and pray?. Computer. Volume
32, Issue 1, Jan. 1999. Page(s):135-138.
[Bat04] Batory, D. Feature-oriented programming and the AHEAD tool suite. 26th
International Conference on Software Engineering (ICSE 2004). 23-28
May 2004 Page(s):702 - 703.
[BB02] Barbeau, M.; Bordeleau, F. A protocol stack development tool using generative
programming. ACM SIGPLAN/SIGSOFT Conference on Generative
Programming and Component Engineering (GPCE’02), Pittsburgh,
October 6–8, 2002, LNCS 2487, Springer-Verlag (2002), pp. 93–109
[BCRW98] Batory, B.; Chen, G.; Robertson, E.; Wang, T. Design Wizards and Visual
Programming Environments for Generators. Fifth International Conference on
Software Reuse (Victoria, Canada, June 1998). IEEE Computer Society
Press, pp. 255-267.
[BDJR03] Bézivin, J. Dupé, G.; Jouault, F.; Rougui, J. E. First experiments with the
ATL model transformation language: Transforming XSLT into XQuery. In the
online proceedings of the OOPSLA’03 Workshop on Generative
Techniques in the Context of the MDA,
http://www.softmetaware.com/oopsla2003/mda-workshop.html
[BF03] Balanyi, Z.; Ferenc, R. Mining design patterns from C++ source code.
International Conference on Software Maintenance (ICSM 2003).
22-26 Sept. 2003 Page(s):305 – 314.
[BG93] Biffl, S.; Grechenig, T. Degrees of consciousness for reuse of software in practice:
Maintainability, balance, standardization. Seventeenth Annual Internacional
Computer Software and Applications Conference, COMPSAC 93. 1-5
Nov. 1993. Page(s):107 – 114.
[BJY01] Bieman, J.M.; Jain, D.; Yang, H.J. OO design patterns, design structure, and
program changes: an industrial case study. IEEE International Conference on
Software Maintenance, 7-9 Nov. 2001 Page(s):580 – 589.
[BLM92] Brown, D.; Levine, J.; Mason, T. lex & yacc. O'Reilly Media, Inc.; 2nd
edition, 1992.
[BMRSS96] Buschmann, F.; Meunier, R.; Rohnert, H.; Sommerlad, P.; Stal, M.
Pattern-Oriented Software Architecture. A System of Patterns. John Wiley &
Sons, 1996.
[Bre05] Breu, S. Extending Dynamic Aspect Mining with Static Information. Fifth IEEE
218 Bibliografía
[BSWM+03] Bieman, J.M.; Straw, G.; Wang, H.; Munger, P.W.; Alexander, R.T. Design
patterns and change proneness: an examination of five evolving systems.
Ninth International Software Metrics Symposium. 3-5 Sept. 2003.
Page(s):40 – 49.
[CBUE02] Czarnecki, K.; Bednasch, T.; Unger, P.; Eisenecker, U.W. Generative
programming for embedded software: An industrial experience report. ACM
SIGPLAN/SIGSOFT Conference on Generative Programming and
Component Engineering (GPCE’02), Pittsburgh, October 6–8, 2002,
LNCS 2487, Springer-Verlag (2002), pp. 156–172
[CD94] Cook, S.; Daniels, J. Designing Object Systems: Object-Oriented Modeling with
Syntropy. Prentice Hall, 1994.
[Chi03] Chiang, C. Towards software reuse using parameterized formal specifications. IEEE
Bibliografía 219
[CHU04] Czarnecki, K.; Helsen, S.; Eisenecker, U. Staged Configuration Using Feature
Models. Software Product Lines Conference (SPLC). Boston, MA, USA,
August 30-September 2, 2004, pp. 266-283.
[CHW98] Coplien, J.; Hoffman, D.; Weiss, D. Commonality and Variability in Software
Engineering. IEEE Software, pp. 37-45, Dec. 1998.
[CLDG+05] Costagliola, G.; De Lucia, A.; Deufemia, V.; Gravino, C.; Risi, M. Design
Pattern Recovery by Visual Language Parsing. Ninth European Conference on
Software Maintenance and Reengineering (CSMR 2005). 21-23 March
2005 Page(s):102 – 111.
[Cle01] Cleaveland, J. C. Program Generators with XML and Java. Prentice Hall,
2001.
[CN99] Clements, P. A Framework for Software Product Lines Practice, Version 2.0.
Report from the Product Line System Program, Software Engineering
Institute, Pittsburg, PA, July 1999, www.sei.cmu.edu/plp
[Deu89] Deutsch, L. P. Design reuse and frameworks in the Smalltalk-80 system. In Ted
J. Biggerstaff and Alan J. Perlis, editors, Software Reusability, Volume II:
Applications and Experience, pages 57–71. Addison-Wesley, Reading,
MA, 1989.
[DHO01] Demuth, B.; Hussmann, H.; Obermaier, S. Experiments with XMI based
transformations of software models. In Workshop on Transformations in
UML. 2001.
[DS05] Dallal, J. A.; Sorenson, P. Reusing class-based test cases for testing object-oriented
framework interface classes. Journal of Software Maintenance and Evolution:
Research and Practice. Volume 17, Issue 3, Date: May/June 2005,
Pages: 169-196.
[DuB06] DuBois, P. Using the Ruby DBI Module. Document revision: 1.03. Last
update: 2006-11-28. http://www.kitebird.com/articles/ruby-dbi.html
[Eck00] Eckel, B. Thinking in C++, 2nd ed. Volume 1. Prentice Hall, 2000.
[FBBO+99] Fowler, M.; Beck, K.; Brant, J.; Opdyke, W.; Roberts, D. Refactoring:
Bibliografía 221
[FBFL05] Ferenc, R.; Beszedes, A.; Fulop, L.; Lele, J. Design Pattern Mining Enhanced
by Machine Learning. 21st IEEE International Conference on Software
Maintenance (ICSM'05). Page(s):295 – 304.
[FKGS04] France, R. B.; Kim, D.-K.; Ghosh, S.; Song, E. A UML-based pattern
specification technique. IEEE Transactions on Software Engineering,
Volume 30, Issue 3, March 2004. Page(s):193 – 206.
[Fow03] Fowler, M. UML Distilled: A Brief Guide to the Standard Object Modeling
Language. Third Edition. Addison Wesley, 2003.
[GFA98] Griss, M.; Favaro, J.; d’ Alessandro, M. Integrating feature modeling with the
RSEB. Fifth International Conference on Software Reuse (ICSR), IEEE
Computer Society Press (1998), pp. 76–85.
[GHJV94] Gamma, E.; Helm, R.; Johnson, R.; Vlissides, J. Design Patterns: Elements of
Reusable Object-Oriented Software. Addison Wesley, 1994.
[GI93] Girardi, M. R.; Ibrahim, B. A software reuse system based on natural language
specifications. 5th Int. Conf. on Computing and Information. Sudbury,
Ontario, Canada, p. 507-511, 1993.
222 Bibliografía
[GR04] Gray, J.; Roychoudhury. A Technique for Constructing Aspect Weavers Using a
Program Transformation System. International Conference on Aspect-
Oriented Software Development (AOSD). Lancaster, UK, March 22-27,
2004, pp. 36-45.
[Har02] Harold, E. R. Processing XML with Java: A Guide to SAX, DOM, JDOM,
JAXP, and TrAX. Addison-Wesley, 2002.
[Hay04] Haywood, D. MDA: Nice Idea, Shame About the… The ServerSide.Com.
http://www.theserverside.com/articles/article.tss?l=MDA_Haywood
[HC05] Huang, C.; Chen, W. Y. A Semi-Automatic Generator for Unit Testing Code
Files Based on JUnit. IEEE International Conference on Systems, Man and
Cybernetics. Volume 1, 10-12 Oct. 2005 Page(s):140 - 145.
[HFR99] Harrison, N.; Foote, B.; Rohnert, H. Pattern Languages of Program Design 4.
Addison Wesley Publishing Company, 1999.
[HML03] Heuzeroth, D.; Mandel, S.; Lowe, W. Generating design pattern detectors from
pattern specifications. 18th IEEE International Conference on Automated
Software Engineering. 6-10 Oct. 2003. Page(s):245 – 248.
[HUn06] http://httpunit.sourceforge.net
Bibliografía 223
[JS00] Jarzabek, S.; Seviora, R. Engineering components for ease of customisation and
evolution. IEE Proceedings- Software. Volume 147, Issue 6, Dec. 2000,
pp. 237 – 248.
[KCHN+90] Kang, K.; Cohen, S.; Hess, J.; Nowak, W.; Peterson, S. Feature-oriented
domain analysis (FODA) feasibility study. Technical Report CMU/SEI-
90TR-21, Software Engineering Institute, Carnegie Mellon University,
Pittsburgh, PA (1990)
[KHHK+01] Kiczales, G.; Hilsdale, E.; Hugunin, J.; Kersten, M.; Palm, J.; Griswold,
W. G. An overview of AspectJ. European Conference on Object Oriented
Programming, 2001.
[Kim02] Kim H. AspectC#: an AOSD implementation for C#. M. Sc. Thesis, Comp.
Sci., Trinity College, Dublin, Noviembre 2002.
99tr022abstract.html
[KMHC05] Kim, S. D.; Min, H. G.; Her, J.S.; Chang, S.H. DREAM: a practical product
line engineering using model driven architecture. Third International Conference
on Information Technology and Applications, (ICITA 2005). Volume 1,
4-7 July 2005. Page(s):70 – 75.
[KRW02] Kirk, D.; Roper, M.; Wood, M. Defining the problems of framework reuse.
26th Annual International Computer Software and Applications
Conference (COMPSAC 2002). 26-29 Aug. 2002. Page(s):623 – 626.
[KSBM99] Kwon, O. C.; Shin, G. S.; Boldyreff, C.; Munro, M. Maintenance with reuse:
an integrated approach based on software configuration management. Sixth Asia
Pacific Software Engineering Conference (APSEC '99). 7-10 Dec. 1999.
Page(s):507 – 515.
[KSRP99] Keller, R.; Schauer R.; Robitaille, S.; Page, P. Pattern-Based Reverse-
Engineering for Design Components. Twenty-First International Conference
on Software Engineering, pages 226-235, Los Angeles, CA, May 1999.
[KWB03] Kleppe, A.; Warmer, J.; Bast, W. MDA Explained. The Model Driven
Architecture: Practice and Promise. Addison-Wesley, 2003.
[LG84] Lanergan, L. G.; Grasso, C. A. Software Engineering with reusable designs and
code. IEEE Trans. Software Engineering, vol 10, no. 5, Sept. 1984.
Page(s): 498-501.
[LKL02] Lee, K.; Kang, K.C.; Lee, J. Concepts and guidelines of feature modeling for
product line software engineering. Software Reuse: Methods, Techniques, and
Tools: Proceedings of the Seventh Reuse Conference (ICSR7), Austin,
USA, Apr.15–19, 2002. LNCS 2319, Springer-Verlag (2002), pp. 62–77
[LL97] Lee, N. Y.; Litecky, C.R. An empirical study of software reuse with special
attention to Ada. IEEE Transactions on Software Engineering,
Volume 23, Issue 9, Sept. 1997. Page(s):537 – 549.
[LMV97] Lam, W.; McDermid, T.A.; Vickers, A.J. Ten steps towards systematic
requirements reuse. Requirements Engineering, 1997., Proceedings of the
Third IEEE International Symposium on 6-10 Jan. 1997. Page(s):6 – 15.
[Lon98] Lonngren, D.D. Reducing the cost of test through reuse. AUTOTESTCON '98.
IEEE Systems Readiness Technology Conference. 24-27 Aug. 1998
Page(s):48 – 53.
[MB01] McNatt, W. B.; Bieman, J. M. Coupling of design patterns: common practices and
their benefits. 25th Annual International Computer Software and
Applications Conference (COMPSAC 2001). 8-12 Oct. 2001. Page(s):574
– 579.
[MB02] Mellor, S. J.; Balcer, M. J. Executable UML: A Foundation for Model Driven
Architecture. Addison-Wesley, 2002.
[MB97] Mattsson, M.; Bosch, J. Framework composition: problems, causes and solutions.
Technology of Object-Oriented Languages and Systems, 1997. TOOLS
23. 28 July-1 Aug. 1997. Page(s):203 – 214.
226 Bibliografía
[MCL04] Mak, J. K. H.; Choy, C. S. T.; Lun, D. P. K. Precise modeling of design patterns
in UML. International Conference on Software Engineering (ICSE
2004). 26th 23-28 May 2004. Page(s):252 – 261.
[MDA03] The Object Management Group. MDA Guide Version 1.0.1. 12th June
2003. http://www.omg.org/mda/
[Mil56] Miller, G. A. The Magical Number Seven, Plus or Minus Two: Some Limits on
Our Capacity for Processing Information. The Psychological Review, 1956, vol.
63, pp. 81-97
[Mil95] Mili, H.; Mili, F.; Mili, A. Reusing software: issues and research directions.
IEEE Transactions on Software Engineering, Volume 21, Issue 6, June
1995. Page(s):528 – 562.
[MKKW99] Mannion, M.; Keepence, B.; Kaindl, H.; Wheadon, J. Reusing single system
requirements from application family requirements. International Conference on
Bibliografía 227
[MMWO94] von Mayrhauser, A.; Mraz, R.; Walls, J.; Ocken, P. Domain based testing:
increasing test case reuse. IEEE International Conference on Computer
Design: VLSI in Computers and Processors, 1994. ICCD '94.
10-12 Oct. 1994 Page(s):484 – 491.
[MOF06] The Object Management Group. Meta Object Facility Core Specification
version 2.0. OMG document formal/2006-01-01
[MRS02] Morisio, M.; Romano, D.; Stamelos, I. Quality, productivity, and learning in
framework-based development: an exploratory case study. IEEE Transactions on
Software Engineering, Volume 28, Issue 9, Sept. 2002. Page(s):876 –
888.
[MSPB+00] Morisio, M.; Seaman, C.; Parra, A.; Basili, V.; Kraft, S.; Condon, S.
Investigating and improving a COTS-based software development. 22nd
International Conference on Software Engineering (ICSE), June 4-11,
2000. Limerick, Ireland. Pages: 32-41.
[MSSA+02] MacDonald, S.; Szafron, D.; Schaeffer, J.; Anvik, J.; Bromling, S.; Tan, K.
Generative design patterns. 17th IEEE International Conference on
Automated Software Engineering (ASE 2002). 23-27 Sept. 2002.
Page(s):23 – 34.
[MSU98] Masuda, G.; Sakamoto, N.; Ushijima, K. Applying design patterns to decision
tree learning system. Proc. ACM SICSOFT Int. Symp. Foundations of
Sofware Engineering, pages 111-120, 1998.
[MSUW04] Mellor, S. J.; Scott, K.; Uhl, A.; Weise, D. MDA Distilled. Principles of
Model-Driven Architecture. Addison-Wesley, 2004.
[Mye89] Myers, W. Allow Plenty of Time for Large-Scale Software. IEEE Software,
Volume 6, Issue 4, July 1989. Pages: 92-99.
[OT01] Ossher, H.; Tarr, P. Multi-dimensional separation of concerns and the hyperspace
approach. Proc. of the Symposium on Software Architecture and
Component Technology: The State of the Art in Software Development.
Kluwer.
[Pel02] Peltier, M.L. MTrans, a DSL for model transformation. Sixth International
Enterprise Distributed Object Computing Conference (EDOC '02). 17-
20 Sept. 2002. Page(s):190 – 199.
[PUTB+01] Prechelt, L.; Unger, B.; Tichy, W.F.; Brossler, P.; Votta, L.G. A controlled
experiment in maintenance: comparing design patterns to simpler solutions. IEEE
Transactions on Software Engineering, Volume 27, Issue 12, Dec. 2001
Page(s):1134 – 1144.
[QVT05] The Object Management Group. MOF QVT Final Adopted Specification.
OMG ptc/05-11-01
[RJ00] Rising, L.; Janoff, N.S. The Scrum software development process for small teams.
IEEE Software. Volume 17, Issue 4, July-Aug. 2000. Page(s):26 – 32.
[RUn06] RubyUnit.
http://homepage1.nifty.com/markey/ruby/rubyunit/index_e.html
[SF04] Shao, J.; Far, B. H. Development of an Intelligent System for Architecture Design
and Analysis. IEEE Canadian Conference on Electrical and Computer
Engineering (CCECE 2004), pp. 539-542, May 2004.
[Sil03] Silva, A.R. The XIS approach and principles. 29th Euromicro Conference. 1-
6 Sept. 2003. Page(s):33 – 40.
[Sim99] Simonyi, C. The Future is Intentional. Computer. Volume 32, Issue 5, May
1999 Page(s):56 – 57.
[SLB00] Shull, F.; Lanubile, F.; Basili, V.R. Investigating Reading Techniques for Object-
Oriented Framework Learning. IEEE Trans. Software Eng., vol. 26, no. 11,
Nov. 2000.
[SLU05] Spinczyk, O.; Lohmann, D.; Urban, M. AspectC++: an AOP Extension for
Bibliografía 231
[SMB96] Shull, F.; Melo, W. L.; Basili, V. R. An Inductive Method for Discovering
Desing Patterns from Object-Oriented Software Systems. Technical report, 1996.
[Spi06] Spinczyk, O. AC++ Compiler Manual. Version 1.1, March 15, 2006.
http://www.aspectc.org
[SSRG+05] Solberg, A.; Simmonds, D.; Reddy, R.; Ghosh, S.; France, R. Using aspect
oriented techniques to support separation of concerns in model driven development.
29th Annual International Computer Software and Applications
Conference (COMPSAC 2005). Volume 1, 26-28 July 2005 Page(s):121 -
126 Vol. 2.
[TH01] Thomas, D.; Hunt, A. Programming Ruby. The Pragmatic Programmers’ Guide.
Addison Wesley, 2001.
[TMQH+03] Trowbridge, D; Mancini, D.; Quick, D.; Hohpe, G.; Newkirk, J.;
Lavigne, D. Enterprise Solution Patterns Using Microsoft .NET. Microsoft
Corporation, June 2003.
http://msdn.microsoft.com/practices/compcat/default.aspx?pull=/libr
ary/en-us/dnpatterns/html/esp.asp
[TPSG+02] Teboul, L.; Pawlak, R.; Seinturier, L.; Gressier-Soudan, E.; Becquet, E.
AspectTAZ : a new approach based on aspect oriented programming for object
oriented industrial messaging services design. 4th IEEE International Workshop
232 Bibliografía
[TRHM+04] Trowbridge, D.; Roxburgh, U.; Hohpe, G.; Manolescu, D.; Nadhan, E.
G. Integration Patterns. Microsoft Corporation, June 2004.
http://msdn.microsoft.com/practices/compcat/default.aspx?pull=/libr
ary/en-us/dnpag/html/intpatt.asp
[TSS04] Turki, S.; Soriano, T.; Sghaier, A. An MDA application for a virtual reality
environment. IEEE International Conference on Industrial Technology
(IEEE ICIT '04). Volume 2, 8-10 Dec. 2004. Page(s):807 – 812.
[Vok04] Vokac, M. Defect frequency and design patterns: an empirical study of industrial
code. IEEE Transactions on Software Engineering, Volume 30, Issue 12,
Dec. 2004 Page(s):904 – 917.
[VV00] Viega, J.; Vuas, J. Can aspect-oriented programming lead to more reliable software?
IEEE Software, Volume 17, Issue 6, Nov.-Dec. 2000 Page(s):19 – 21.
[VVP02] Varro, D.; Varro, G.; Pataricza, A. Designing the automatic transformation of
visual languages. Science of Computer Programming, vol. 44(2):pp. 205--
227, 2002.
[WK03] Warmer, J.; Kleppe, A. The Object Constraint Language: Getting Your Models
Ready for MDA. Second Edition. Addison Wesley, 2005.
[WZZ03] Wang, H.; Zhang, D.; Zhou, J. MDA-based development of e-learning system.
27th Annual International Computer Software and Applications
Conference (COMPSAC 2003). 3-6 Nov. 2003. Page(s):684 – 689.
[XMI05] The Object Management Group. XML Metadata Interchange (XMI), v2.1.
OMG document formal/2005-09-01
[YLM04] Yu, Y.; Leite, J. C. S. P.; Mylopoulos, J. From goals to aspects: discovering
aspects from requirements goal models. 12th IEEE International Requirements
Engineering Conference, 2004. Page(s):38 – 47.
[ZBJ04] Zhu, L.; Babar, M. A.; Jeffery, R. Mining patterns to support software
architecture evaluation. Fourth Working IEEE/IFIP Conference on
Software Architecture.
12-15 June 2004 Page(s):25 – 34.
[ZLB04] Zhang, Z.; Li, Q.; Ben, K. A new method for design pattern mining.
International Conference on Machine Learning and Cybernetics. Volume
3, 26-29 Aug. 2004 Page(s):1755 – 1759.