Está en la página 1de 107

Arquitectura de software

dirigida por modelos


(Model-Driven Architecture)
Liliana Favre
UNCPBA
2006
Técnicas de reingeniería y MDA
Bibliografía
Las gráficas fueron extraídas de
1. Demeyer, S.; Ducasse, S.; Nierstrasz, O.
Object-Oriented Reengineering Patterns,
Morgan-Kaufmann Publishers, 2002
2. Systa, Tarja. Static and Dynamic Reverse
Engineering for Java Software Systems. Ph. D.
University Tempere, Finland, 2000
3. Tonella, P. Potrich, A. Reverse Engineering of
Object-Oriented Code, Springer, 2005
Ingeniería directa, inversa y
reingenieía
Ingeniería directa, inversa y
reingeniería
Ingeniería directa
Es el proceso que transforma diseños en un alto nivel de abstracción a la
implementación de un sistema.
Ingeniería inversa
Es el proceso que analiza un sistema para identificar sus componentes y
sus interrelaciones y crea representaciones del sistema en otra forma o en
un mayor nivel de abstracción.
Reingeniería
Es el proceso que transforma una representación de bajo nivel en otra,
mientras reconstituye artefactos de mayor nivel de abstracción a lo largo
del proceso. Es decir, transforma implementaciones concretas en otras
concretas transformando al sistema en todos sus niveles.
Otras definiciones relacionadas a
reingenieía
Reestructuración
Es el proceso de transformar una representación en otra en el
mismo nivel de abstracción, preservando el comportamiento
externo del sistema.
Refactoring
Es el proceso de reestructuración en un contexto orientado a
objetos
Mantenimiento
El proceso de modificación de un producto de software para
corregir fallas, mejorar la performance o adaptarlo a un
cambio de entorno.
Otras definiciones relacionadas a
reingeniería
Evolución de software
La evolución de software se caracteriza por la existencia de un
código del sistema y la actividad típica es la implementación de
un cambio que puede ser requerido para:
 corregir el software ( mantenimiento correctivo)
 agregar funcionalidad ( mantenimiento perfectivo)
 adaptarlo a un cambio de ambiente ( mantenimiento adaptativo)
 reestructurarlo para facilitar su futuro mantenimiento
( mantenimento preventivo)
Durante la evolución, la descripción más precisa y confiable del
software es su código.
Ingeniería inversa
Ingeniería inversa
(reverse engineering)
 Las técnicas de ingeniería inversa permiten analizar y
representar software en una forma abstracta a fin de facilitar
mantenimiento, reingeniería, reusabilidad y documentación.
 Proveen una manera de extraer vistas de alto nivel desde el
código sistema, que resumen aspectos relevantes de la
computación ejecutada por las sentencias del programa.

Chikofsky y Cross(*) definen a la ingeniería inversa como el


proceso de analizar un sistema target con dos objetivos:
 Identificar los componentes del sistema y sus interrelaciones
 Crear representaciones del sistema en otra forma o en un
mayor nivel de abstracción

* Reverse Engineering and design Recovery: A taxonomy. IEEE


Software, January 1990
Ingeniería inversa
(reverse engineering)

Ingeniería inversa estática +


Ingeniería inversa dinámica

Análisis estático
Describe la estructura del software a partir del código (texto)
Análisis dinámico
Describe el comportamiento en ejecución del software.
Extraer información en código OO es difícil (o imposible?)
 Naturaleza dinámica de los lenguajes OO
 Creación de objetos, garbage collector, dynamic binding,…
Ingeniería inversa
Inconvenientes
 La única fuente confiable es el código que
está poco (o nada) documentado y se ha
perdido contacto con los diseñadores y/o
programadores.
 Las herramientas CASE proveen buen
soporte para análisis estático pero limitado
para análisis dinámico.
Ingeniería inversa de programas OO
Ingeniería inversa de código OO
Inconvenientes
Por ejemplo, las construcciones

UML JAVA no se alinean con UML:


 Generalización/
especialización
 Agregación/composición
 Asociaciones
JAVA
 Cuerpo de los métodos

Propuesta de :

Tonella, P. Potrich, A. Reverse Engineering of


Object-Oriented Code. Springer, 2005

Describe ingeniería inversa de diagramas UML de clases,


objetos, interacción, estados y paquetes. Propone
especializaciones de algoritmos de data-flow basados en una
estructura llamada OFG (Object Flow Graph).
Arquitectura general de una
herramienta de ingeniería inversa
Ejemplo de modelo del lenguaje para
JAVA simplificado
Ingeniería inversa estática

OFG: representación básica para el análisis estático.


Permite seguir el flujo de información de objetos desde su creación,
a través de asignaciones a variables, su uso en invocaciones de
métodos, almacenamiento en atributos de una clase,...
El tipo de información que se propaga en el OFG depende del
objetivo del análisis en el que se use.

 Un lenguaje abstracto
 Un algoritmo de propagación de flujo que es genérico en el tipo de
información procesada
Ingeniería inversa estática
Análisis estático sobre JAVA
 Sensible al flujo de datos
 Insensible al flujo de control
 Representación por grafos de flujos de objetos basada
en una versión abstracta, simplificada de JAVA que
omite sentencias de control (condicionales, iteraciones,
etc)
 Evita conflictos de nombres (los identificadores son
precedidos por un punto separados por la lista de
packages, clases y métodos que los incluyen)
Ingeniería inversa estática
¿Porqué el análisis es sensible al flujo de datos/
insensible al flujo de control?

 Complejidad computacional
 Naturaleza de LOO
 Puede automatizarse
OFG
Lenguaje abstracto
OFG
Lenguaje abstracto
OFG
Lenguaje abstracto

return y this
Ejemplo
eLib- Diagrama de clases
Ejemplo
Lenguaje abstracto-Declaraciones

Library.Library()
Declaración de constructor
implícito
Library.loans
Declaración de atributo
loans
Ejemplo
Lenguaje abstracto-Declaraciones

Library.borrowDocument(Library.borrowDocument.user,
Library.borrowDocument.doc)
Declaración de método
Ejemplo
Lenguaje abstracto-Sentencias

60-62
Library.borrowDocument.loan =
new Loan(Library.borrowDocunent.user,
Library.borrowDocument.doc);
Library.borrowDocument.this. Library.
addLoan(library.borrowDocument.loan)
Ejemplo
Lenguaje abstracto-Sentencias

42
Library.addLoan.user=Loan.getUser.return
43
Library.addLoan.doc =
Loan.getDocument.return;
Generación del OFG

OFG = (N, E) N: conjunto de nodos E: conjunto de arcos


Para cada variable local, atributo o parámetro formal, se agrega
un nodo a OFG.
Por ejemplo, el OFG de la clase Library de eLib contiene:
 Un nodo asociado al atributo loan rotulado Library.loans
 Dos rótulos asociados con los parámetros formales del método
borrowDocument rotulados
Library.borrowDocument.user
Library.borrowDocument.doc
 La variable local loan asociada con el nodo rotulado
Library.borrowDocument.loan
 El objeto current en BorrowDocument está asociado con un
nodo rotulado
Library.borrowDocument.this
Generación del OFG

Los arcos se insertan de acuerdo a las siguientes reglas


Generación del OFG

Library.borrowDocument.loan = new Loan(Library.borrowDocument.user,


Library.borrowDocument.doc)
Library.borrowDocument.this.Library.addLoan (Library.borrowDocument.loan)
genera los siguientes arcos:
Generación del OFG
x = y {(y,x) є E}

genera los siguientes arcos


Generación del OFG
Containers
Si una función de librería introduce un flujo de dato de
una variable x a otra y, debería incluirse el arco (x,y)
 Clases que implementan la interfaz Collection (vector,
linkedList, HashSet, TreeSet,..)
 Clases que interpretan la interfaz Map (Hashtable, HashMap,
TreeMap,…)
Los dos casos básicos
(1) c.insert(x) (x,c) є E
(2) x = c.extract() (c,x) є E
Los mismos arcos son los que serían introducidos en el OFG en
presencia de las siguientes asignaciones
(1) c = x
(2) x = c
Generación del OFG
Containers

Library.loans = Library.addLoan.loan
Generación del OFG
Containers

Library.printAllLoans.i = Library.loans
- el flujo desde el container(loans) al iterador i
Libray.printAllLoans.loan =Library.printAllLoans.i;
- El acceso al objeto contenido ( invocación de next) y la
asignación a la variable local loan
Generación del OFG
Containers
Generación del OFG
Containers

- Un objeto user es insertado en el container users


Library.users = Library.addUser.user

- Un objeto user es extraído del container users


Library.getUser.return = Library.users
Algoritmo Data-Flow
forward
Algoritmo Data-Flow forward
La solución provista por el algoritmo es
conservativa(segura), ningún flujo de datos
que no esté contemplado ocurre. Sin embargo,
es imposible decidir estáticamente si un
camino es factible o no.
Análisis insensible a objetos
OFG para análisis “sensible” a
objetos
En un análisis sensible a objetos los atributos de clase
no estáticos y los métodos(incluyendo sus parámetros y
variables locales) se replican para cada objeto
identificado estáticamente.
Para cada punto de asignación, se crea un identificador de
objeto y todos los atributos y métodos en la clase son
replicados.
Construcción es más complicada ( se analizará más adelante)
OFG
“Sensible” a objetos

Asumiendo que dos objetos son creados Loan1


y Loan2
OFG
“Sensible” a objetos

Parte de código
dentro del método
main de la clase Main

5 objetos son asignados en el fragmento de código:


User1, Document1, Loan1, Document2, Loan2
OFG
Data-flow

Insensible a objetos
OFG
Data-Flow

Sensible a objetos
OFG
Data-Flow
OFG
Data-Flow
OFG
Data-Flow
OFG
Data-Flow
OFG
Data-Flow
OFG
Data-Flow
Desde JAVA a diagramas de clases
Desde JAVA a
diagramas de clase
Los diagramas de clase resumen importantes decisiones
de
diseño sobre la organización del sistema.

Elementos de un diagrama de clases


 Clases
 Interfaz, clases abstractas, clases parametrizadas
 Relaciones de dependencia, generalización y
asociación
 Especificaciones OCL
Desde JAVA a diagramas de clase
Algoritmo basado en el análisis sintáctico del código
Inconvenientes
a) Las relaciones entre clases llevan información semántica
que no puede ser inferida del análisis del código.

b) Los tipos declarados son una “aproximación” de las clases


instanciadas en el programa debido a la presencia de
relaciones de herencia y al uso de interfaces.

Algoritmo basado en OFG para resolver a) y b)


Extracción de clases
La información mostrada en una clase (atributos,
métodos, el tipo de los atributos, los parámetros
de los métodos, su visibilidad y alcance) puede
extraerse analizando la sintaxis.
Ejemplo
Extracción de clases
Ejemplo
Extracción de clases
Ejemplo
Extracción de clases
Ejemplo
Extracción de clases
Extracción de relaciones
Ejemplo
Extracción de dependencias

Dependencia entre Library y User


Ejemplo
Extracción de dependencias

Dependencia entre Library y


Document
Ejemplo
Extracción de asociaciones

Loan está asociada a


User y Document
Tipos actual vs. declarados
Los tipos declarados de atributos, variables locales y parámetros de
los métodos son usados para determinar la clase target de
asociaciones y dependencias. Es bastante común que el tipo
declarado sea la raíz de una jerarquía de herencia o que sea una
interfaz.
Ejemplo

InternalUser es una subclase de User.


Book, Journal, TechnicalReport son subclases de Document
Si la aplicación usa sólo una parte del subárbol de herencia, el target de la
asociación o dependencia puede ser incorrecto. Por ejemplo en una instancia
específica una asociación debería conectar Loan y Book en vez de Document.
Tipos actuales vs. declarados
Ejemplo
Una aplicación puede contener una clase BinaryTreeNode
con un atributo obj que almacena información asociada con
cada nodo del árbol y su tipo declarado Comparable, la
interfaz implementada para objetos que pueden ordenarse
totalmente por el método compareTO. Esto generaría la
asociación entre BinaryTreeNode a Comparable. Si la
aplicación define a una clase Student que implementa a
Comparable, el método propuesto no recupera la asociación
entre BinaryTreeNode y Student
El método propuesto no detecta que los tipos declarados
pueden ser una superclase del tipo del objeto actual o una
interfaz implementada para el objeto actual.
Data-Flow
Especialización para determinar el
tipo de objetos actuales
Ejemplo
Árbol binario de búsqueda
Ejemplo
Árbol binario de búsqueda
Ejemplo-Árbol binario de búsqueda
Sintaxis abstracta
Ejemplo-Árbol binario de búsqueda
OFG
Ejemplo-Árbol binario de búsqueda
OFG

Es detectada una asociación entre BinaryTreeNode y


Student
Containers
Clases que implementan estructuras de datos
(Listas, árboles grafos, vectores, …)
Los containers débilmente tipados coleccionan objetos
cuyos tipos no son declarados, por ejemplo, Containers en
versiones de JAVA que no soportan genericidad.
Esto dificulta la ingeniería inversa dado que la
información acerca de las clases de los objetos
contenidos no se encuentra disponible en el código.
Containers

Map y Collection son interfaces. Las clases que las implementan son
HashMap y LinkedList, dos containers débilmente tipados, que son clases de
biblioteca y no serán explicitadas
Ejemplo
Data-Flow forward para Containers
Data-Flow backward para Containers
Ejemplo
Containers
Ejemplo
Containers
Ejemplo
Containers- Sintaxis abstracta
Ejemplo
Containers- Sintaxis abstracta
Ejemplo
Containers- Sintaxis abstracta
Ejemplo
Containers- OFG
Desde JAVA a diagrama de objetos
Ingeniería inversa de diagramas de
objetos
El diagrama de objetos muestra una “instantánea”
de un conjunto de objetos y sus relaciones.
Los elementos en este diagrama (objetos y relaciones)
son instancias de los elementos (clases y asociaciones)
en el diagrama de clases.
El diagrama de objetos muestra explícitamente
cómo diferentes instancias pueden desempeñar
diferentes roles y estar involucradas en diferentes
relaciones.
Ingeniería inversa de diagrama de
objetos
Análisis estático + análisis dinámico
 Un análisis estático del código basado en OFG para
aproximar estáticamente la creación de objetos y sus
interrelaciones.
 Un análisis dinámico basado en trazas de ejecución
donde cada una de ellas está asociada con un
diagrama de objetos y las relaciones que son
instanciadas.
El análisis estático es seguro con respecto a los objetos y
relaciones que representa, sin embargo, no provee información
sobre multiplicidad y no permite detectar caminos que no son
factibles
Extracción de diagramas de objetos
 Los puntos de asignación (líneas de código que contienen sentencias de
asignación) son usados para aproximar el conjunto de objetos creados por
un programa, mientras que el OFG es usado para determinar las relaciones
entre objetos.
Especialización de la propagación del flujo de datos
Extracción de diagramas de objetos
Cada sitio de asignación ( 5) está asociado con un único
identificador ci (el nombre de la clase c y un entero
incrementado i).
Cada identificador de objeto ci genera un nodo en el
diagrama de objetos. Cada nodo en OFG asociado a un
atributo, es decir con un prefijo c y sufijo a, donde a es
un atributo de clase c, se tiene en cuenta cuando se generan
asociaciones entre objetos.
El conjunto out de tal nodo de OFG( es decir out[c.a]) da el
conjunto de objetos alcanzables desde todos los objetos ci de
clase c a lo largo de la asociación implementada a través del
atributo a.
Ejemplo
Árbol binario de búsqueda- JAVA
Ejemplo
Árbol de búsqueda-Sintaxis abstracta
Ejemplo
Árbol de búsqueda-Sintaxis abstracta
Los objetos de tipo BinaryTreeNode se asignan
en 3 puntos distintos del programa, originando 3
identificadores: BinaryTreeNode1,
BinaryTreeNode2, BinaryTreeNode3, los que
están incluidos en el conjunto gen de
BinaryTree.root, BinaryTreeNode.addLeft.n,
BinaryTreeNode.addRight.n respectivamente.
Hay una única asignación para objetos BinaryTree,
el único identificador es BinaryTree1, insertado en el
gen de BinaryTree.main.bt
Ejemplo
Árbol binario de búsqueda- OFG
Ejemplo
Árbol binario de búsqueda - OFG
Construcción del diagrama de objetos
Cada identificador se convierte en un nodo en el
diagrama.Los conjuntos out de los atributos de
clase determinan las interelaciones
Ejemplo
Árbol binario de búsqueda - OFG
Extracción del diagrama de objetos
Análisis “sensible” a objetos
Un identificador de objeto ci se asocia a cada punto de
asignación en el programa que se analiza.
Una ubicación n originalmente alcanzada por una clase c,
se asocia ahora a un conjunto de nodos n´, alcanzados por
identificadores de objetos ci. Específicamente, para cada
identificador de un objeto ci, creado para la clase c, una
réplica de la ubicación n alcanzada por ci se inserta en el
OFG
Análisis “sensible” a objetos
Reglas para la construcción del OFG
Extracción del diagrama de objetos
Ejemplo- Árbol de búsqueda
Extracción del diagrama de objetos
Ejemplo- Árbol de búsqueda
Ejemplo- Árbol de búsqueda
OFG
Ejemplo- Árbol de búsqueda
OFG Sensible a objetos
Ejemplo- Árbol de búsqueda
OFG Sensible a objetos
Ejemplo- Árbol de búsqueda
OFG Sensible a objetos
Ejemplo- Árbol de búsqueda
OFG

Insensible a objetos Sensible a objetos


Ejemplo- Árbol de búsqueda
OFG
El análisis “sensible” a objetos da información
precisa sobre los elementos de datos almacenado
en los dos árboles binarios bt1 y bt2. El OFG
indica que el primer árbol es usado para
administrar objetos de clase A y el segundo
de tipo B1. El análisis “insensible” es menos
preciso y no distingue los elementos almacenados
en los dos árboles
Análisis dinámico
Un análisis dinámico provee un conjunto de
diagramas de objetos, cada uno asociado con un
test. Las ejecuciones de un programa se asocian
a una traza de ejecución de cuyo análisis surge
un diagrama de objetos.
Es parcial, está basado en un caso limitado de
casos de prueba
Ejemplo- Árbol de búsqueda
Análisis dinámico
Asumamos que los elementos del árbol están ordenados
de acuerdo a compareTo disponible para el atributo object
(en la clase BinaryTreeNode) que implementa una interfaz
Comparable.
Un test puede consistir en la creación de 1 o más objetos
BinaryTreeNode, con un String asignado al atributo object
y la inserción de los nodos en el mismo árbol.
Supongamos 3 cadenas(“a”, “b”, “c”) para test TC1, TC2
y TC3
Análisis dinámico
Análisis dinámico
Ejemplo- Árbol de búsqueda
Análisis dinámico

También podría gustarte