|
|
|
Computación |
Glosarios -
Biografías
-
Textos |
|
1. Introducción y conceptos básicos.
2. Técnica de modelado de objetos (OMT). 2.1. Fases. 2.2. Modelos. 2.3. Diferencias con la metodología estructurada. 3. Casos de uso. 4. Modelo de objetos. 5. Modelo dinámico. 6. Modelo funcional. 7. Relación entre los modelos. 8. Método de análisis. 5.1. Introducción y conceptos básicos. Desde comienzos de la década de los 80, el paradigma "orientado a objetos" ha ido madurando como un enfoque de desarrollo de software alternativo a la programación estructurada o modular. Se empezó a crear diseños de aplicaciones de todo tipo usando una forma de pensar orientada a los objetos, y a implementar estos diseños utilizando lenguajes orientados a objetos. Sin embargo, el análisis de requisitos se quedó atrás. No se desarrollaron técnicas de análisis específicamente orientadas a objetos. Esta situación ha ido cambiando poco a poco, a medida que se desarrollaban técnicas de análisis específicas para desarrollar software orientado a objetos, e incluso como complemento de otros métodos de análisis. Ejemplos de estas nuevas técnicas son los métodos de Coad/Yourdon, Jacobson, Booch y Rumbaugh (OMT) . En este tema seguiremos principalmente esta última metodología. El Análisis Orientado a Objetos (AOO) se basa en conceptos sencillos, conocidos desde la infancia y que aplicamos continuamente: objetos y atributos, el todo y las partes, clases y miembros. Puede parecer llamativo que se haya tardado tanto tiempo en aplicar estos conceptos al desarrollo de software. Posiblemente, una de las razones es el éxito de los métodos de análisis estructurados, basados en el concepto de flujo de información, que monopolizaron el análisis de sistemas software durante los últimos veinte años. En cualquier caso, el paradigma orientado a objetos ha sufrido una evolución similar al paradigma de programación estructurada: primero se empezaron a utilizar los lenguajes de programación estructurados, que permiten la descomposición modular de los programas; esto condujo a la adopción de técnicas de diseño estructuradas y de ahí se paso al análisis estructurado. El paradigma orientado a objetos ha seguido el mismo camino: el uso de la Programación Orientada a Objetos (POO) ha modificado las técnicas de diseño para adaptarlas a los nuevos lenguajes y ahora se están empezando a utilizar técnicas de análisis basadas en este nueva forma de desarrollar software. El AOO ofrece un enfoque nuevo para el análisis de requisitos de sistemas software. En lugar de considerar el software desde una perspectiva clásica de entrada/proceso/salida, como los métodos estructurados clásicos, se basa en modelar el sistema mediante los objetos que forman parte de él y las relaciones estáticas (herencia y composición) o dinámicas (uso) entre estos objetos. Este enfoque pretende conseguir modelos que se ajusten mejor al problema real, a partir del conocimiento del llamado dominio del problema, evitando que influyan en el análisis consideraciones de que estamos analizando un sistema para implementarlo en un ordenador. Desde este punto de vista, el AOO consigue una abstracción mayor que el análisis estructurado, que modela los sistemas desde un punto de vista más próximo a su implementación en un ordenador (entrada/proceso/salida). Este intento de conocer el dominio del problema ha sido siempre importante; no tiene sentido empezar a escribir los requisitos funcionales de un sistema de control de tráfico aéreo, y menos aún diseñarlo o programarlo sin estudiar primero qué es el tráfico aéreo o qué se espera de un sistema de control de este tipo. La ventaja del AOO es que se basa en la utilización de objetos como abstracciones del mundo real. Esto nos permite centrarnos en los aspectos significativos del dominio del problema (en las características de los objetos y las relaciones que se establecen entre ellos) y este conocimiento se convierte en la parte fundamental del análisis del sistema software, que será luego utilizado en el diseño y la implementación. Este enfoque no es totalmente nuevo, sino que puede considerarse como una extensión del modelado de datos (DER) que se utiliza en los métodos estructurados. Sin embargo, el modelado de datos mediante DER está más orientado al diseño de bases de datos y se centra exclusivamente en la identificación de los datos que maneja un sistema y en las relaciones estáticas que se establecen entre esos datos. En el AOO, los objetos encapsulan tanto atributos como procedimientos (operaciones que se realizan sobre los objetos), e incorpora además conceptos como el polimorfismo o la herencia que facilitan la reutilización de código. El uso de AOO puede facilitar mucho la creación de prototipos, y las técnicas de desarrollo evolutivo de software. Los objetos son inherentemente reutilizables, y se puede crear un catálogo de objetos que podemos usar en sucesivas aplicaciones. De esta forma, podemos obtener rápidamente un prototipo del sistema, que pueda ser evaluado por el cliente, a partir de objetos analizados, diseñados e implementados en aplicaciones anteriores. Y lo que es más importante, dada la facilidad de reutilización de estos objetos, el prototipo puede ir evolucionando hacia convertirse en el sistema final, según vamos refinando los objetos de acuerdo a un proceso de especificación incremental. 5.1.1. Deficiencias del análisis estructurado.
5.1.2. Ventajas del AOO.
|
|
|
5.1.3. Conceptos básicos. Las técnicas orientadas a objetos se basan en organizar el software como una colección de objetos discretos que incorporan tanto estructuras de datos como comportamiento. Esto contrasta con la programación convencional, en la que las estructuras de datos y el comportamiento estaban escasamente relacionadas. Las características principales del enfoque orientado a objetos son, en primer lugar: Identidad. Los datos se organizan en entidades discretas y distinguibles llamadas objetos. Estos objetos pueden ser concretos o abstractos, pero cada objeto tiene su propia identidad. Dicho de otra forma: dos objetos son distintos incluso aún en el caso de que los valores de todos sus atributos (p. ej. nombre y tamaño) coincidan. Dos manzanas pueden ser totalmente idénticas pero no por eso pierden su identidad: nos podemos comer una u otra. Clasificación. Los objetos que tengan los mismos atributos y comportamiento se agrupan en clases. Todas las manzanas tienen una serie de atributos comunes: tamaño, peso, grado de maduración, y un comportamiento común: podemos coger una manzana, moverla o comerla. Los valores de los atributos podrán ser distintos para cada una de ellas, pero todas comparten los mismos atributos y comportamiento (las operaciones que se pueden realizar sobre ellas). Una clase es una abstracción que describe propiedades (atributos y comportamiento) relevantes para una aplicación determinada, ignorando el resto. La elección de clases es arbitraria, y depende del dominio del problema. Según esto, una clase es una abstracción de un conjunto posiblemente infinito de objetos individuales. Cada uno de estos objetos se dice que es una instancia o ejemplar de dicha clase. Cada instancia de una clase tiene sus propios valores para sus atributos, pero comparte el nombre de estos atributos y las operaciones con el resto de instancias de su clase. Polimorfismo. El polimorfismo permite que una misma operación pueda llevarse a cabo de forma diferente en clases diferentes. Por ejemplo, la operación mover, es distinta para una pieza de ajedrez que para una ficha de parchís, pero ambos objetos pueden ser movidos. Una operación es una acción o transformación que realiza o padece un objeto. La implementación específica de una operación determinada en una clase determinada se denomina método. Según lo dicho, una operación es una abstracción de un comportamiento similar (pero no idéntico) en diferentes clases de objetos. La semántica de la operación debe ser la misma para todas las clases. Sin embargo, cada método concreto seguirá unos pasos procedimentales específicos. Herencia. El concepto de herencia se refiere a la compartición de atributos y operaciones basada en una relación jerárquica entre varias clases. Una clase puede definirse de forma general y luego refinarse en sucesivas subclases. Cada clase hereda todas las propiedades (atributos y operaciones) de su superclase y añade sus propiedades particulares. La posibilidad de agrupar las propiedades comunes de una serie de clases en una superclase y heredar estas propiedades en cada una de las subclases es lo que permite reducir la repetición de código en el paradigma OO y es una de sus principales ventajas. 5.2. Técnica de modelado de objetos (OMT). La esencia del desarrollo de software OO es la identificación y organización de conceptos del dominio del problema, más que en su implementación final usando un determinado lenguaje. La Técnica de Modelado de Objetos (OMT, Rumbaugh, 1991) es un procedimiento que se basa en aplicar el enfoque orientado a objetos a todo el proceso de desarrollo de un sistema software, desde el análisis hasta la implementación. Los métodos de análisis y diseño que propone son independientes del lenguaje de programación que se emplee para la implementación. Incluso esta implementación no tiene que basarse necesariamente en un lenguaje OO. OMT es una metodología OO de desarrollo de software basada en una notación gráfica para representar conceptos OO. La metodología consiste en construir un modelo del dominio de aplicación y ir añadiendo detalles a este modelo durante la fase de diseño. OMT consta de las siguientes fases o etapas. 5.2.1. Fases.
Algunas clases que aparecen en el sistema final no son parte del análisis sino que se introducen durante el diseño o la implementación. Este es el caso de estructuras como árboles, listas enlazadas o tablas hash, que no suelen estar presentes en el dominio de aplicación. Estas clases se añaden para permitir utilizar determinados algoritmos. Los conceptos del paradigma OO pueden aplicarse durante todo el ciclo de desarrollo del software, desde el análisis a la implementación sin cambios de notación, sólo añadiendo progresivamente detalles al modelo inicial. 5.2.2. Modelos. Al igual que los métodos estructurados, OMT utiliza tres tipos de modelos para describir un sistema:
Los tres modelos son vistas ortogonales (independientes) del mismo sistema, aunque existen relaciones entre ellos. Cada modelo contiene referencias a elementos de los otros dos. Por ejemplo, las operaciones que se asocian a los objetos del modelo de objetos figuran también, de forma más detallada en el modelo funcional. El más importante de los tres es el modelo de objetos, porque es necesario describir qué cambia antes que decir cuándo o cómo cambia. 5.2.3. Diferencias con los métodos estructurados. OMT invierte el método estructurado, en el que se da más importancia a la descomposición funcional del sistema y, por tanto, a los diagramas de proceso. Este enfoque de descomposición funcional puede parecer que lleva de forma más directa a una implementación del sistema, pero con frecuencia este sistema suele ser más frágil. Si cambian los requisitos, un sistema basado en descomposición funcional puede requerir una reestructuración masiva. Por el contrario, el enfoque OO se centra en primer lugar en identificar los objetos del dominio de aplicación y después en establecer procedimientos que los manejen. Aunque esto puede parecer más indirecto, el software OO se mantiene mejor ante los cambios de requisitos, porque se basa en la estructura subyacente del dominio de aplicación, en vez de en los requisitos funcionales de un determinado problema. De todas formas, los modelos son los mismos que en el análisis estructurado. El modelo de objetos, el único que parece nuevo, es bastante similar a los DERs. Las diferencias principales consisten en la mayor importancia que se da el modelo de datos, por encima de los otros dos, y en el enfoque orientado a objetos de este modelo (encapsulando tanto datos como operaciones) en vez de estar orientado a bases de datos como los DERs. En este sentido, puede compararse a los paquetes de lenguajes como ADA, con los que se implementan TADs. La diferencia reside en que en la POO, la herencia y el polimorfismo favorecen la reutilización de código y la programación por especialización, desde una perspectiva distinta al uso de paquetes genéricos en ADA. 5.3. Casos de uso. Una forma de describir los requisitos iniciales del usuario, durante la fase de conceptualización, es construir casos de uso del sistema, descritos inicialmente por Jacobson en 1987 y actualmente incorporados a la mayor parte de las metodologías de AOO. Un caso de uso está formado por una serie de interacciones entre el sistema y un actor (una entidad externa, ejerciendo un rol determinado), que muestran una determinada forma de utilizar el sistema. Cada interacción comienza con un evento inicial que el actor envía al sistema y continua con una serie de eventos entre el actor, el sistema y posiblemente otros actores involucrados.
Un caso de uso puede ser descrito en lenguaje natural, mediante trazas de eventos o mediante diagramas de interacción de objetos. 5.4. Modelo de objetos. El modelo de objetos describe la estructura de los objetos de un sistema: su identidad, sus relaciones con otros objetos, sus atributos y sus operaciones. Los cambios y las transformaciones no tienen sentido a menos que haya algo que cambiar o transformar. OMT considera este modelo el más importante de los tres. El modelo de objetos se representa gráficamente con diagramas de objetos y diagramas de instancias, que contienen clases de objetos e instancias, respectivamente. Las clases se disponen en jerarquías que comparten una estructura de datos y un comportamiento comunes, y se relacionan con otras clases. Cada clase define los atributos que contiene cada uno de los objetos o instancias y las operaciones que realizan o sufren estos objetos. 5.4.1. Elementos del modelo de objetos. El modelo de objetos puede contener los siguientes elementos: Objetos o instancias. Un objeto es un concepto, una abstracción o una cosa con unos límites definidos y que es relevante para el problema en cuestión. Los modelos de objetos sirven tanto para obtener un conocimiento mejor del dominio de aplicación como de base para la implementación del sistema en un ordenador. Una característica de los objetos es que tienen identidad y son distinguibles. Aunque dos objetos tengan los mismos valores para todos sus atributos son diferentes. El término objeto está sobrecargado. Mediante él podemos referirnos tanto a clases de objetos (p. ej. el concepto abstracto mesa) como a las instancias de estas clases (una mesa determinada). Es mejor utilizar los términos clase e instancia para evitar confusiones. La mayoría de las instancias de una clase derivan su individualidad de tener valores diferentes en alguno/s de sus atributos o de tener relaciones con instancias diferentes. No obstante pueden existir instancias con los mismos valores de los atributos e idénticas relaciones. El símbolo gráfico para representar instancias es un rectángulo de esquinas redondeadas. Dentro del rectángulo figura la clase a la que pertenece la instancia (entre paréntesis) y los valores de sus atributos.
Las instancias figuran en diagramas de instancias, que se utilizan normalmente para describir ejemplos que aclaren un diagrama de objetos complejos o para describir escenarios determinados (p. ej. situaciones típicas o anómalas, escenarios de prueba, etc.). Clases. Una clase o clase de objetos es una abstracción que describe un grupo de instancias con propiedades (atributos) comunes, comportamiento (operaciones) común, relaciones comunes con otros objetos y (lo que es más importante) una semántica común. Así un caballo y un establo tienen los dos un coste y una edad, pero posiblemente pertenezcan a clases diferentes (aunque esto depende del dominio de aplicación: en una aplicación financiera ambos pertenecerían posiblemente a la misma clase: Inversiones). La diferencia entre instancia y clase está en el grado de abstracción. Un objeto es una abstracción de un objeto del mundo real, pero una clase es una abstracción de un grupo de objetos del mundo real. La abstracción permite la generalización y evita la redefinición de las características (atributos, comportamiento o relaciones) comunes, de forma que se produce una reutilización de estas definiciones comunes por parte de cada uno de los objetos. Por ejemplo todas las elipses (instancias) comparten las mismos operaciones para dibujarlas o calcular su área. El símbolo gráfico para representar clases es un rectángulo, en el que figura el nombre de la clase. Las clases se representan en los diagramas de clases, que son plantillas que describen un conjunto de posibles diagramas de instancias. Describen, por tanto el caso general.
Atributos. Un atributo es un dato contenido en todas las instancias de una clase. Cada atributo tiene un valor para cada una de las instancias. Varias clases pueden tener atributos comunes (p. ej. nombre, en las clases Persona y Calle) pero cada atributo debe ser único dentro de una clase. Los atributos tienen que ser datos, no objetos. La diferencia entre unos y otros reside en la identidad: los objetos tienen identidad, pero los atributos no. Por ejemplo, todas las ocurrencias del valor ‘3’ de un atributo son indistinguibles. Los atributos se representan en el segundo área de los símbolos de clase e instancia. En las clases, figurará el nombre del atributo, el tipo y el valor por defecto. En las instancias, el valor del atributo para ese objeto determinado.
Operaciones. Una operación o método es una función o transformación. Cada operación lleva implícito un objeto destino, sobre el que se va a realizar la operación. El comportamiento de la operación depende de la clase del objeto destino. Todos los objetos de una clase comparten las mismas operaciones o métodos. Cada objeto conoce la clase a que pertenece y, por tanto, la implementación correcta de la operación. Una misma operación puede aplicarse a objetos de clases distintas. En este caso diremos que la operación es polimórfica, y a la implementación de la operación en cada una de las clases la llamaremos método. Una operación puede tener una serie de argumentos explícitos, además del objeto destino, que actúa siempre como argumento implícito.
Las operaciones figuran en la tercer área del símbolo de las clases. Opcionalmente figuran también la lista de argumentos y el tipo de resultado de la operación (si es que la operación devuelve algún resultado). En los símbolos de instancia no figuran las operaciones. Enlaces. Un enlace es una conexión entre dos o más instancias (objetos). Los enlaces pueden ser considerados como las instancias de las asociaciones.
Asociaciones. Una asociación es una abstracción de un grupo de enlaces con una estructura común (todos ellos conectan instancias de objetos de las mismas clases) y una semántica común (todos los enlaces tienen el mismo significado. Una asociación describe un conjunto de enlaces potenciales de la misma forma que una clase describe un conjunto de instancias potenciales.
Tanto los enlaces como las asociaciones se representan mediante arcos, generalmente etiquetados con el nombre de la asociación. Aunque la etiqueta induce a leer una asociación en un determinado sentido, las asociaciones pueden recorrerse en cualquier dirección. Los enlaces y las asociaciones pueden conectar más de dos objetos. En este caso se representan mediante un rombo con conexiones a cada uno de los objetos:
La mayoría de las asociaciones conectan solamente dos clases de objetos y, siempre que sea posible usaremos asociaciones binarias porque son más sencillas de nombrar, entender y de implementar Sin embargo habrá casos en los que no podamos desglosar una asociación n-aria en varias asociaciones binarias. En el ejemplo anterior si desglosamos la asociación en tres (Programador - conoce - Lenguaje, Proyecto - se implementa en - Lenguaje, y Programador - participa en - Proyecto), no podemos representar que un Programador participa en un Proyecto programando en un Lenguaje determinado de lo que conoce. Multiplicidad. Cada asociación puede modelar la conexión un número indeterminado de objetos de las clases que conecta. Para representar el número de instancias de cada clase que pueden participar en una asociación utilizaremos la siguiente notación en cada extremo de la asociación: ¡ Opcional. La asociación puede relacionar 0 ó 1 instancias de la clase · Muchos. Significa de 0 a N. 3 Exactamente 3. 2,4 Dos o cuatro. 2-4 De dos a cuatro. 4+ Más de cuatro. Exactamente 1. Una multiplicidad mayor que uno (p. ej. la existente entre las clases Clase1 y Clase2 abajo) indica que para cada instancia del Clase1 pueden existir muchas instancias de la asociación (muchos enlaces) que lo conecten a instancias de la Clase2.
Con frecuencia la multiplicidad no viene expresada en la especificación preliminar, por lo que tendremos que decidirla en base al uso que queremos hacer del sistema. No es conveniente abusar de relaciones del tipo muchos a muchos pues, aunque son las más generales, van a sobrecargar el sistema a la hora de la implementación. De todas formas, no hay que preocuparse mucho de la multiplicidad en las etapas iniciales del análisis, cuando tengamos un mayor conocimiento del dominio del problema será más fácil decidir sobre ella. Atributos de una asociación. Al igual que los objetos contienen atributos, las asociaciones pueden contenerlos también. (p. ej. asociación pide entre Clientes y Artículos, que tiene un atributo cantidad). Estos atributos no pertenecen a ninguna de las clases relacionadas por lo que no hay más remedio que representarlos en la asociación. Estos atributos tendrán un valor propio para cada una de las instancias de la asociación (enlaces).
No obstante, si la asociación tiene multiplicidad 1/1 o 1/M podemos incluir estos atributos en la clase que tiene multiplicidad 1. Por ejemplo, en la relación Persona - compra - Casa, el atributo fecha de compra. puede incluirse en la clase Casa. Sin embargo, si la asociación es M/M, no podemos hacer esto: por ejemplo en un sistema de multipropiedad una casa puede pertenecer a varias personas, que la compran en fechas distintas. En cualquier caso no es conveniente incluir los atributos de las asociaciones en alguna de las clases relacionadas, puesto que reducimos la flexibilidad de nuestro modelo, que tendrá que ser reestructurado en caso de que necesitemos cambiar la multiplicidad. Una asociación con atributos se parece bastante a una clase. En determinados casos (si la asociación contiene también operaciones o si mantiene asociaciones con otras clases podemos modelarla como una clase. Calificación. Un tipo especial de asociaciones son las asociaciones calificadas. Si queremos expresar mediante el modelo que ‘un directorio contiene varios ficheros, cada uno de ellos identificados mediante un nombre’ podemos hacer una asociación 1/M normal, con nombre fichero como atributo de Fichero (1)
De esta forma, partiendo del directorio podemos acceder a todos sus ficheros mediante la asociación múltiple, pero así no expresamos que el nombre identifica a los ficheros dentro del directorio. La solución es utilizar nombre fichero como calificador en el extremo opuesto al M de la asociación (2), en este caso desaparece la multiplicidad en la asociación, puesto que utilizando el nombre la asociación múltiple se convierte en unitaria y, además, el modelo contiene una información mucho más exacta.
La calificación reduce la multiplicidad pero en algunos casos no se logra conseguir una multiplicidad unitaria. Tomemos como ejemplo el calificador cargo en la plantilla de una empresa. Suponemos que varias personas pueden ocupar el mismo cargo (3)
En algunos casos podemos considerar al calificador como un atributo especial del objeto situado al otro extremo de la asociación (1), o también podemos considerar la asociación calificada como una forma de asociación ternaria (4) o asociación con atributos (5). Por último si consideramos que una misma persona puede ocupar varios cargos distintos en la empresa, no podemos considerar cargo como un atributo de persona (6 *), pues esto significaría que una persona ocupa varias veces el mismo cargo en varias empresas.
En resumen, la calificación nos permite reducir la multiplicidad y tiene una mayor potencia expresiva que las otras dos opciones (considerar el calificador como atributo de una de las clases o incluirlo como atributo de la asociación) por lo que optaremos por la asociación calificada cuando nos encontremos ante situaciones similares a las descritas.
Roles o papeles. Para mayor claridad podemos etiquetar los extremos de las asociaciones con el rol o papel que representa la clase en la asociación. Así, en la relación trabaja, las personas juegan el papel de empleados mientras que las empresas juegan el papel de contratantes.
Restricciones Las restricciones son relaciones funcionales entre entidades de un modelo de objetos. El término entidad incluye objetos, clases, atributos, enlaces y asociaciones. Una restricción restringe los valores que una entidad puede tomar. Ejemplos:
Las restricciones simples pueden situarse en el modelo de objetos; restricciones complejas aparecerán en el modelo funcional. Las restricciones no tienen porqué aparecer inicialmente en el modelo de objetos, estas irán añadiéndose conforme se vaya concretando en la definición del modelo. Las restricciones se escriben entre llaves y se colocan junto a la entidad restringida. Las restricciones se expresan en lenguaje natural o con ecuaciones. En caso de que existan varias entidades involucradas en una restricción, dibujaremos una línea de puntos entre las entidades. (Ejemplo: una asociación puede ser subconjunto de otra: el presidente de una comisión debe ser un miembro de la comisión -la asociación presidente_de es un subconjunto de la asociación miembro_de).
5.4.2. Relaciones de composición o agregación. La composición nos permite representar las relaciones de se compone de o es una parte de que existen entre los objetos del modelo. Podemos utilizar asociaciones normales para representar la composición (y así lo hemos hecho en alguno de los ejemplos anteriores) pero la relación de composición tiene unas características especiales, por lo que se ha incluido un tipo de asociación especial para representarla. Estas características son que la composición es transitiva, mientras que las relaciones normales no lo son. Por otra parte, la composición es antisimétrica y, por último, algunas propiedades del conjunto se propagan o influyen a las partes (p. ej. Un robot móvil con un brazo articulado: la posición del manipulador en el extremo del brazo depende no sólo de la posición (ángulos de rotación del brazo) sino también de la posición de todo el robot móvil. La relación de composición se expresa gráficamente con un rombo al extremo de la asociación (p. ej. los Documentos se componen de Párrafos, que a su vez se componen de Caracteres). Para modelar un objeto que se compone de objetos de varias clases distintas (p. ej. una cebra que se compone de una cabeza, un cuerpo, hasta cuatro patas y una cola opcional) podemos agrupar las relaciones de composición en una única asociación ramificada, indicando la multiplicidad en cada extremo.
A veces, la aplicación de una operación a un objeto supone la aplicación automática de esta operación a un conjunto de objetos. A esto se le llama propagación.. La operación de copiar se propaga del documento a sus párrafos y de estos a sus caracteres. La operación sin embargo no se propaga en la dirección inversa, el hecho de copiar un párrafo no implica la copia del documento. Agregación y Asociación Según lo visto, la agregación no es más que un tipo (frecuente) de asociación, que tiene unas características determinadas y que se representa en OMT mediante una notación gráfica especial. La diferencia entre asociación y agregación es fundamentalmente semántica, y hay que tener esto presente a la hora de decidir modelar una relación entre clases como asociación o como agregación. Por ejemplo, una Empresa es una agregación de Divisiones, que a su vez son agregaciones de Departamentos. Sin embargo, no es una agregación de Personas, puesto que Empresa y Persona son objetos independientes.
5.4.3. Generalización. El último tipo de relación puede presentarse entre las clases es la relación de generalización o especialización (depende desde dónde se mire). La generalización es una forma de abstracción que permite modelar que varias clases comparten una serie de características comunes, a la vez que tienen características propias. En el paradigma OO la generalización se relaciona con el mecanismo de herencia, mediante el que los objetos heredan las características comunes (atributos y operaciones) y es la fuente principal de la reutilización de código. Generalización o especialización es la relación que se establece entre una clase y una o más versiones refinadas de esta clase. La clase general se denomina superclase y las clases especializadas, subclases.
En la superclase definiremos los atributos y operaciones comunes a todas las subclases. En cada subclase definiremos los atributos y métodos específicos para esa subclase. El mecanismo de herencia garantiza que todas las subclases heredan los atributos y operaciones definidos en la superclase. La relación de generalización puede modelarse en varios niveles y es transitiva: las instancias de una subclase heredan los atributos y operaciones de todas las clases antecesoras. Además, cada instancia de una subclase puede considerarse también una instancia de cada una de las clases antecesoras: una lista ordenada doblemente encadenada es también una lista doblemente encadenada y una lista. Si hay varias clases que particularizan una dada, podemos modelar esto mediante una sola relación de generalización ramificada. En ocasiones, existe un atributo de la superclase cuyos valores determinan la adscripción de un objeto a una u otra de las subclases. Un atributo de este tipo se denomina discriminante. Redefinición. Las subclases no sólo heredan los atributos y las operaciones de la superclase sino que también pueden redefinirlos. Esto se hace cuando un método general definido en una superclase no puede aplicarse tal como está a algunas de las subclases o puede realizarse una implementación más eficiente del método (p. ej. en la jerarquía anterior podemos redefinir el método girar. De la misma forma, las clases que hereden de Polígono redefinirán el método área. La redefinición no puede cambiar la semántica de la característica redefinida (la operación tiene que ser la misma, aunque el método cambie) ni puede cambiar la signatura (es decir la forma: el número y tipo de los parámetros) de la característica redefinida. Algunos lenguajes de POO permiten que la redefinición restrinja el tipo de los parámetros (que sea por ejemplo un subrango). Clases Abstractas Una clase abstracta es una clase que no tiene instancias directas pero cuyas clases descendientes (clases concretas) sí pueden tener instancias directas. Una clase concreta puede tener subclases abstractas, pero éstas han de tener subclases concretas (las hojas de una jerarquía de clases han de ser clases concretas). Las clases abstractas organizan características comunes a varias clases. A veces es útil crear una superclase abstracta que encapsule aquellas características (atributos, operaciones y asociaciones) comunes a un conjunto de clases. Puede ser que esta clase abstracta aparezca en el dominio del problema o bien que se introduzca artificialmente para facilitar la reutilización de código. Normalmente las clases abstractas se usan para definir métodos que son heredados por sus subclases. Sin embargo, una clase abstracta puede definir el protocolo de una determinada operación sin proporcionar el correspondiente método. Esto de denomina operación abstracta, que define la forma de una operación para la que cada subclase concreta debe suministrar su propia implementación. Una clase concreta no puede tener operaciones abstractas porque los objetos de esta clase tendrían operaciones indefinidas. Para indicar que una operación es abstracta se coloca una restricción {abstracta} junto a ella.
Herencia múltiple La herencia múltiple permite a una clase tener más de una superclase, y así heredar las características de todos sus padres. Esto complica las jerarquías de herencia, que dejan de ser árboles para convertirse en grafos. La gran ventaja de la herencia múltiple es el incremento en las posibilidades de reutilización. El inconveniente es la pérdida de simplicidad conceptual y de implementación. La herencia múltiple presenta dos problemas: conflictos y herencia repetida. Cuando una clase hereda simultáneamente sus propiedades de varias clases, pueden existir en estas propiedades representadas por el mismo identificador con significados diferentes; a esto se le denomina conflicto. Los conflictos que provoca la herencia múltiple se agravan en presencia de situaciones en las que una clase es heredada por otra por medio de caminos distintos; es lo que se denomina herencia repetida.
Existen muchas propuestas para resolver este tipo de problemas, casi tantas como lenguajes orientados a objetos. Podemos encontrar desde sistemas que dejan al usuario su resolución (algunas versiones de Smalltalk), hasta los que prohiben situaciones conflictivas (C++, Eiffel), pasando por sistemas que usan algún heurístico linealizando el árbol de herencia (CommonLoops), o los que simplemente evitan el problema impidiendo la herencia múltiple (Java). Las ventajas e inconvenientes de las posibles alternativas de la herencia múltiple se han estudiado con cierta profundidad, llegando a la conclusión de que cada alternativa tiene razones a favor y en contra, lo que impide tomar decisiones de diseño que satisfagan todo tipo de exigencias. En la mayoría de los casos, la interpretación que se da a la herencia múltiple depende más de su adecuación a la implementación que a otros motivos. 5.4.4. Consejos prácticos. Por último, una serie de consejos prácticos para realizar modelos de objetos. Muchos de estos consejos son muy similares a los de los métodos estructurados: lo importante de una notación gráfica es que represente de forma correcta y clara el dominio de aplicación.
5.5. Modelo dinámico. 5.5.1. Diagramas de estados. El modelo dinámico del método OMT se corresponde con el modelo de control o modelo de comportamiento de las técnicas de análisis estructurado. En ambos casos, el modelo se representa mediante DEs, pero en el caso de OMT estos DEs se utilizan para modelar el comportamiento de cada clase de objetos, es decir, para modelar el comportamiento común a todas las instancias de una clase. Al igual que todas las instancias de una clase comparten atributos, pero tienen valores particulares para esos atributos, todas las instancias de una clase se comportan de igual forma, pero pueden encontrarse en estados distintos. Los elementos que figuran en un DE ya son conocidos, aquí sólo vamos a referirnos a las diferencias con respecto al análisis estructurado. Estados y eventos. Por estado de un objeto entendemos los valores de los atributos y los enlaces que mantiene un objeto en un momento determinado. Los objetos interactúan unos con otros y como consecuencia de esas interacciones cambian de estado (es decir, cambian el valor de sus atributos o sus enlaces con otros objetos). Los estados que figuran en los DEs son abstracciones de los valores de los atributos y enlaces de un objeto, es decir, engloban conjuntos de valores de los atributos, de forma que muestran situaciones en las que el objeto presenta un determinado comportamiento. Por ejemplo, en el objeto Cuenta Corriente, el estado En rojos engloba todos los estados del objeto en los que el saldo es negativo.
Al definir los estados, debemos ignorar los atributos que no afectan al comportamiento del objeto y agrupar todas las combinaciones de valores de atributos y enlaces en las que el objeto presente el mismo comportamiento. La interacción entre objetos se realiza mediante eventos. Como consecuencia de (la recepción de) un evento, un objeto puede realizar una serie de acciones y/o enviar eventos a otros objetos y/o cambiar de estado. Los eventos son el método de intercambiar información entre los objetos. Esta información consistirá en la mayoría de los casos en una señal lógica, pero en otros puede tratarse de información más compleja, y el evento tendrá una serie de atributos: cancelar evento lógico retirar(numero de cuenta, 5000) evento con atributos La respuesta de un objeto en un estado determinado ante un evento determinado puede depender de los valores exactos de los atributos, pero sólo cuantitativamente. Cualitativamente, la respuesta será la misma para todos los objetos que se encuentren en ese estado. La relación o secuencia de estados, eventos, transiciones y acciones se representa en los DEs. El modelo dinámico es un conjunto de DEs, uno para cada clase de objetos que tenga un comportamiento dinámico no trivial (no es necesario hacer un DE para cada clase sino sólo para aquéllas que lo precisen). La relación entre los distintos DEs se establece mediante los eventos que emiten unos objetos y consumen otros (se emiten en un determinado DE y se consumen en otro). Guardas. La encapsulación propia del paradigma OO impide con carácter general el acceso directo a valores de los atributos de otros objetos. Por este motivo una guarda es una condición que se define sobre los atributos propios de un objeto . Al recibir un evento, la transición etiquetada con dicho evento se dispara si y sólo si se satisface la guarda. Actividades y acciones. Las actividades y las acciones son las respuestas que tiene que dar un objeto ante los eventos que se producen en el exterior o las guardas (condiciones de datos) que se satisfacen internamente. Típicamente, las acciones y las actividades van a ser operaciones definidas en la clase a la que pertenece el objeto, aunque en ocasiones las acciones pueden consistir en enviar un mensaje a otro objeto. Como ya sabemos, una actividad es una operación que necesita un tiempo para completarse, y se corresponde siempre con la ejecución de un determinado método o con el objeto inactivo, esperando a que se cumplan determinadas condiciones. Cuando un objeto entra en un estado, ejecuta la actividad correspondiente hasta que ésta finalice o bien hasta que el disparo de una transición nos haga abandonar dicho estado. Por el contrario, una acción es una operación instantánea, que idealmente no necesita tiempo para realizarse y que se asocia, por tanto, al disparo de una determinada transición. Las acciones generalmente consisten en asignar valores a los atributos del objeto o en generar eventos para que sean tratados por otro proceso (con la notación enviar: evento). Por este motivo, las acciones no suelen corresponderse con operaciones del objeto, sino posiblemente con parte del código de ciertas operaciones. Trazas de eventos. Los eventos son el medio que utilizan los objetos para comunicarse entre sí. Podemos representar la secuencia de eventos que se ha producido entre los objetos de un determinado sistema mediante una traza de eventos. Esta traza es un diagrama que muestra la secuencia de envíos y recepciones de eventos entre varios objetos. Las trazas de eventos son útiles para mostrar cómo un sistema ha llegado a un escenario determinado, y se mostrarían entonces junto con el diagrama de instancias que represente ese escenario, pero también son útiles para construir el modelo dinámico del sistema, pues muestran una secuencia lógica de eventos que podemos utilizar para realizar los DE de cada clase. A partir de varias trazas de eventos podemos construir el DE de una clase. Cambiando el punto de vista, un diagrama de estados nos permite determinar la secuencia de estados por los que pasa un objeto a partir de una traza de eventos. Si el evento recibido figura en alguna de las transiciones que salen del estado, entonces la transición se dispara y el objeto pasa al estado de llegada de la transición. 5.5.2. Composición en los DEs. Una de las características del paradigma OO es la definición de objetos componiendo otros objetos. El DE de un objeto compuesto estará formado por la unión de los DEs de cada uno de los objetos que lo componen. Cada uno de los objetos estará en un estado determinado, por lo que el estado del objeto compuesto estará representado mediante una tupla que contiene cada uno de los estados de los componentes. Es decir, los objetos compuestos tienen estados compuestos. Esta posibilidad nos permite describir la concurrencia, no sólo en los objetos compuestos, sino en cualquier objeto. Podemos descomponer un objeto simple en varias partes, cada una que contiene un subconjunto de sus atributos y enlaces, y realizar un DE para cada uno de esos subconjuntos. Al igual que antes, el estado del objeto vendrá representado por la composición de estos DEs. Cada uno de los componentes de un objeto modelado mediante un DE compuesto evoluciona de forma concurrente al resto. En algunos casos, podemos necesitar mostrar la concurrencia entre las actividades que se realizan dentro de un estado. Podemos encontrarnos con un estado en el que la actividad a realizar pueda ser descompuesta en una serie de pasos concurrentes. En este caso dividiremos el estado en dos (o más) subestados concurrentes.
Ejemplo: Las operaciones Expulsar Dinero y Devolver Tarjeta del cajero automático. La sincronización de las transiciones de salida de cada uno de los subestados se hace juntando estas transiciones para formar la transición de salida del objeto compuesto. 5.5.3. Generalización en los DEs. Generalización de estados. Puede ocurrir que el objeto tenga un comportamiento tan complejo que el DE que lo modele sea demasiado grande y difícil de leer. En estos casos, podemos hacer DEs anidados, en los que un estado del DE padre se corresponda con un DE hijo. En este DE hijo se detalla el estado padre en una secuencia de estados, transiciones, acciones y actividades. Esta forma de anidamiento es similar al anidamiento que se producía en los DEs del análisis estructurado. Podemos utilizar esta posibilidad en el caso de que la actividad que se asocie a un estado pueda ser descompuesta en una serie de operaciones secuenciales. En el DE hijo representaremos cada una de estas operaciones en un nuevo estado. Las transiciones que entran y salen del estado padre deben figurar en los estados iniciales y finales del DE hijo. Alternativamente, podemos utilizar el anidamiento de estados para representar las transiciones comunes a una serie de estados, al igual que hacíamos en el análisis estructurado. Cada estado anidado o subestado comparte las transiciones de salida del estado padre o superestado, por otra parte las transiciones que entran en el padre nos llevan al estado inicial del DE anidado. Cuando un objeto esté en un superestado, habrá de estar necesariamente en uno y sólo en uno de los subestados correspondientes. Generalización de eventos. Al igual que los estados también podemos generalizar los eventos. Podemos agrupar los eventos en clases hasta conseguir una jerarquía de eventos, lo que nos permite utilizar diferentes grados de abstracción en diferentes partes del modelo. 5.5.5. Herencia de DEs. Según hemos dicho a cada clase del modelo de objetos se asocia un DE que modela su comportamiento, pero ¿qué sucede cuando dos clases están relacionadas mediante generalización? Entre las propiedades que se heredan de la clase padre está también el comportamiento, por lo que las subclases heredan el DE de la superclase. Es posible que la subclase añada atributos u operaciones con respecto a la superclase. Entonces podemos considerar el DE de la subclase como la composición del DE de la superclase con el/los DEs que modelen el comportamiento del objeto con respecto a los nuevos atributos, al igual que hacíamos con los objetos compuestos. En otros casos, la especialización del DE consistirá en el refinamiento de alguno de los estados del DE de la superclase, descomponiendo este estado en el DE de la subclase. Esto se corresponde con una división más detallada del espacio de estados heredado (el conjunto de valores de los atributos heredados). En cualquier caso existe la posibilidad de conflicto: si la subclase redefine el comportamiento de los objetos respecto a alguno de los atributos heredados. No se pueden incluir transiciones o estados nuevos en el DE heredado puesto que entonces no habría una correspondencia entre ambos DEs. Estas situaciones, en las que las clases herederas modifican tienen un comportamiento distinto al de la clase padre y no es posible establecer una correspondencia entre ambos, son bastante frecuentes en la práctica. En estos casos no podemos hablar de herencia del DE sino de una redefinición completa del mismo. Esto es una limitación del modelo que no está resuelta y que contrasta con las posibilidades de redefinición de operaciones y atributos al especializar una clase. Esta limitación puede presentarse también cuando el comportamiento relacionado con los atributos nuevos definidos en la clase hija no puede modelarse de forma independiente a los heredados, sino que requiera realizarse de forma conjunta. 5.5.6. Modelo dinámico: consejos prácticos.
En los diagramas anidados hay que tener en cuenta la consistencia, al menos de las transiciones de salida. Las transiciones de salida del estado padre deben figurar en alguno de los estados del DE hijo. Las de entrada no hace falta pues suponemos que el DE hijo comienza siempre por el estado inicial.
¡ Existen relaciones de composición entre los objetos. El DE del objeto compuesto estará formado por el conjunto de los DEs de los componentes. El estado de un objeto compuesto es una tupla, cuyos elementos son los estados de los objetos componentes. ¡ Existe concurrencia interna en el objeto. Si el objeto es concurrente (si puede o debe realizar varias acciones al mismo tiempo) podemos dividir los estados en subestados concurrentes, en cada uno de ellos se realiza una actividad. ¡ Podemos dividir el comportamiento en facetas independientes. Si el comportamiento presenta facetas que dependen de conjuntos disjuntos de atributos, podemos modelar este comportamiento mediante varios DEs, uno para cada conjunto. Estos DEs serán mucho más sencillos puesto que sólo van a manejar un subconjunto de los eventos, sin que tengamos que preocuparnos de definir transiciones para el resto. ¡ Existen relaciones de herencia entre los objetos. Si el objeto hijo define nuevos atributos, haremos un DE para definir el comportamiento según los valores de estos atributos nuevos. Si el objeto hijo descompone uno o más estados del padre, tendremos un DE anidado.
5.6. Modelo funcional. El modelo funcional describe las computaciones que se realizan en un sistema, mostrando cómo se derivan los valores de salida a partir de los de entrada. El modelo funcional muestra cómo se realizan las operaciones del modelo de datos y las actividades acciones de los DEs. La existencia de un modelo funcional basado en el uso de DFDs distingue OMT de otras metodologías de desarrollo orientado a objetos. Sin embargo, no podemos considerar esto como algo positivo de OMT puesto que no es fácil la adecuación del modelo de procesos del análisis estructurado al tipo de código existente en un sistema orientado a objetos. Por este motivo apenas se usa y, de hecho, escritos recientes de Rumbaugh descartan el uso de DFDs para representar el modelo funcional del sistema, optando, en su lugar, por incluir casos de uso, diagramas de interacción de objetos y especificaciones de operaciones similares a las PSPECs del análisis estructurado. La especificación de una operación puede realizarse mediante precondiciones y postcondiciones. Una precondición indica suposiciones sobre el estado del sistema al comienzo de la operación. Una postcondición describe el estado del sistema una vez realizada la operación. Las precondiciones y postcondiciones pueden expresarse mediante algún lenguaje formal, aunque en la mayoría de los casos basta con utilizar lenguaje natural, siempre que el significado quede claro.
En general, la implementación de las operaciones puede derivarse del modelo de comportamiento durante el diseño del sistema. Sin embargo, durante el análisis, será necesario describir, al menos, las operaciones de aquéllas clases para las que no se ha realizado diagrama de estados. La implementación de una operación puede invocar otras operaciones, pero esto es un aspecto de diseño y no de especificación. Para mostrar la colaboración entre distintos objetos a la hora de realizar una operación determinada podemos realizar un diagrama de interacción de objetos.
5.7. Relación entre los modelos. El modelo de objetos describe la estructura de datos sobre la que operan los modelos funcional y dinámico. Las operaciones en el modelo de objetos se corresponden con las acciones y actividades del modelo dinámico y con los procesos del modelo funcional. El modelo dinámico describe el control de los objetos. El DE describe el comportamiento de los objetos de una clase, mostrando secuencias válidas de cambios de comportamiento en las clases del modelo de objetos. Los estados son clases de equivalencia de los valores de atributos y enlaces del objeto, que normalmente se asocian a la ejecución de una determinada operación en el objeto. Los eventos aparecen también como operaciones en el modelo de objetos. Un subestado refina los valores de atributos y enlaces que puede tener un objeto. Una jerarquía de estados de un objeto es equivalente a una jerarquía de restricciones sobre el objeto. Como los LOO no suelen permitir restricciones de valores sobre el tipo de los atributos heredados o los parámetros de las operaciones (no se puede cambiar la signatura, ni siquiera restringiéndola), el modelo dinámico es el sitio adecuado para representar estas restricciones. En la mayoría de los LOO, las instancias no pueden cambiar la clase a la que pertenecen a lo largo de su vida, pero sí que pueden cambiar de estado. Por tanto, para modelar objetos que van a estar sujetos a restricciones cambiantes, modelaremos estas restricciones como estados en lugar de como clases. Las jerarquías de objetos y de eventos son totalmente independientes. Los eventos sirven para intercambiar información entre objetos de clases distintas. Las transiciones se suelen representar mediante operaciones, cuyo nombre es el evento que dispara la transición. El modelo funcional describe funciones invocadas por operaciones en el modelo de objetos o acciones y actividades en el modelo dinámico. Las funciones operan sobre los valores de datos especificados en el modelo de objetos. El modelo de objetos muestra las entidades que realizan o padecen las funciones. El modelo dinámico muestra la secuencia en la que se realizan las funciones. En resumen:
El modelo funcional muestra las operaciones que se realizan en cada clase y los argumentos de estas operaciones. El modelo dinámico muestra los estados de cada objeto y las operaciones que éstos realizan al recibir eventos y cambiar de estado.
El modelo funcional muestra las definiciones de las acciones y actividades del modelo dinámico. El modelo de objetos muestra los objetos que sufren o realizan las acciones y actividades del modelo dinámico.
El modelo de objetos muestra las entidades que realizan o padecen las funciones del modelo funcional. El modelo dinámico muestra la secuencia en que se realizan las funciones del modelo funcional. 5.8. Método de análisis. El objetivo del AOO es modelar el mundo real, de forma que pueda ser entendido. Es necesario abstraer las características importantes del problema en primer lugar, dejando los detalles para más adelante. Un buen modelo de análisis debe indicar lo que hay que hacer, sin restringir cómo hay que hacerlo, evitando tomar anticipadamente decisiones de implementación. El modelo de análisis se compone del modelo de objetos, que representa la estructura estática de la información, el modelo dinámico, que indica la secuencia de eventos, y el modelo funcional, que muestra las transformaciones de datos. No todos ellos tienen la misma importancia, y la necesidad de desarrollar cada uno de ellos depende del dominio del problema. Habrá casos en que no sea necesario realizar el modelo dinámico o el funcional. El modelo de objetos siempre es necesario si vamos a hacer AOO. El análisis no es una actividad secuencial, sino que los modelos se construyen de forma iterativa, añadiendo nuevas características o modificando el modelo según se refinan los requisitos y también a partir del conocimiento del dominio de aplicación obtenido al construir cada uno de los modelos. Estrictamente, podemos dividir la etapa de análisis dentro de la metodología OMT en tres fases: modelo de objetos, modelo dinámico y modelo funcional. Sin embargo, si consideramos la fase inicial de conceptualización, debemos incluir también los casos de uso. 5.8.1. Casos de uso. Los casos de uso describen las distintas formas de utilizar el sistema, visto desde su exterior, es decir, desde el punto de vista del usuario. Para realizar los diagramas de casos de uso se puede proceder como se describe a continuación:
No debería haber demasiados casos de uso en un sistema. Entre 5 y 10 casos bastan normalmente para mostrar los usos principales del sistema. Si no es así, es necesario utilizar un menor nivel de detalle. En cada caso de uso se debe escoger un nivel de detalle similar.
5.8.2. Modelo de objetos. Empezaremos a modelar el sistema realizando su modelo de objetos. Este modelo muestra la estructura estática de los datos del mundo real y las relaciones entre estos datos. El modelo de objetos precede normalmente al dinámico y al funcional porque normalmente está mejor definido en la especificación preliminar, es menos dependiente de detalles de la aplicación, es más estable respecto a la evolución de la solución y es más fácil de entender que el resto. Los pasos a seguir son los siguientes: Identificar objetos y clases. Durante el proceso de desarrollo aparecen tres categorías de objetos: objetos del dominio, objetos de aplicación y objetos internos. Los objetos del dominio son significativos desde el punto de vista del dominio del problema. Existen de forma independiente a la aplicación y tienen sentido para los expertos del dominio. Los objetos de aplicación representan aspectos computacionales de la aplicación que son visibles para los usuarios. No existen en el espacio del problema, solo tienen sentido en el contexto de la aplicación que vamos a desarrollar para solucionarlo. Sin embargo, no dependen exclusivamente de decisiones de diseño, puesto que son visibles al usuario y no pueden ser cambiados sin alterar la especificación de la aplicación. No pueden obtenerse analizando el dominio de la aplicación, pero sí pueden ser reutilizados de aplicaciones anteriores, incluso aunque sean de diferente dominio. Los objetos de aplicación incluyen controladores, dispositivos e interfaces. Los objetos internos son componentes de la aplicación que resultan invisibles para el usuario. Su existencia se deriva de decisiones de diseño para implementar el sistema. No deben aparecer durante el análisis. Una parte importante del diseño consiste en añadir objetos internos para hacer factible la implementación del sistema. Por tanto, en el modelo de objetos figurarán tanto objetos del dominio como objetos de aplicación. Su construcción puede ser realizada en dos fases: En una primera fase podemos construir un modelo que represente los objetos del dominio del problema y las relaciones que existen entre ellos. Este modelo equivale a lo que se llama modelo esencial en la terminología del análisis estructurado. En una segunda fase podemos construir un modelo de aplicación, completando el modelo anterior con objetos de aplicación. Para esto jugarán un papel muy importante los casos de uso desarrollados durante la conceptualización del problema. Los objetos del modelo pueden ser tanto entidades físicas (como personas, casas y máquinas) como conceptos (como órdenes de compra, reservas de asientos o trayectorias). En cualquier caso, todas las clases deben ser significativas en el dominio de aplicación. Hay que evitar definir clases que se refieren a necesidades de implementación como listas, subrutinas o el reloj del sistema. No todas las clases figuran explícitamente en la especificación preliminar, algunas están implícitas en el dominio de aplicación o son de conocimiento general. Podemos empezar haciendo una lista de clases candidatas a partir de la especificación preliminar. Normalmente las clases se corresponden con nombres en este documento. No hay que preocuparse aún de establecer relaciones de generalización/especialización entre las clases. Esto se hace para reutilizar código y estas relaciones aparecen más claramente cuando se definan atributos y operaciones. Una vez que tenemos una lista de clases candidatas hay que proceder a revisarla, eliminando las incorrectas, siguiendo los siguientes criterios:
Una vez identificadas las clases debemos empezar a preparar el diccionario de datos del sistema. Cada clase figurará como una entrada en el diccionario donde se define brevemente su significado y las funciones que realiza. El diccionario de datos también contiene entradas para las asociaciones, las operaciones y los atributos, que deben ser definidos según las vamos incorporando al modelo. Identificar asociaciones (incluyendo las de composición) entre los objetos. Cualquier relación entre dos o más clases debe ser modelada como una asociación. No hay que incluir en las clases punteros o atributos que se refieran a objetos de otras clases. Todas estas relaciones deben modelarse como asociaciones para que quede constancia de ellas en el modelo de objetos. Las relaciones de composición también son asociaciones, simplemente tienen unas características particulares. Hay que distinguir entre unas y otras pero no merece la pena discutir mucho sobre ello. Una vez identificadas las asociaciones candidatas, procederemos a revisar esta lista, eliminado algunas según los siguientes criterios:
Una vez revisada la lista, procederemos a completar el modelo de objetos, indicando para cada asociación que lo necesite:
Identificar atributos y operaciones. El siguiente paso es identificar los atributos y operaciones de cada clase y de los enlaces que los contengan. Los atributos describen propiedades de los objetos, tales como peso, color o edad, pero no son objetos. Cualquier relación entre objetos debe ser modelada como una asociación, no como un atributo de los objetos relacionados. Tampoco hay que incluir como atributos de los objetos los calificadores de las asociaciones ni los atributos de los enlaces. Con frecuencia los atributos no figuran en la especificación preliminar. Lo normal es partir de un conjunto básico de atributos para cada objeto e ir añadiendo otros según los vayamos necesitando. Organizar y simplificar las clases mediante relaciones de generalización y especialización. El paso siguiente es organizar las clases en jerarquías de forma que se puedan abstraer las características comunes. Esto puede hacerse en dos direcciones:
Aunque la herencia facilita la reutilización de código, hay que evitar abusar demasiado de estas relaciones. Aplicaremos la especialización sólo cuando sea necesario, es decir, cuando la distinción entre dos clases especializadas sea significativa para el problema. Esto sucederá cuando ambas clases tengan atributos o operaciones distintos. Verificar los caminos de acceso. A continuación hay que comprobar los caminos de acceso en el modelo de objetos. Se trata de comprobar si todas las asociaciones necesarias están presentes en el diagrama, de forma que se pueda hacer un recorrido entre los objetos relacionados. También hay que comprobar la multiplicidad: para relaciones con multiplicidad M, ¿es necesario acceder a las instancias relacionadas de forma individualizada? ¿existe algún mecanismo para hacerlo?, ¿existe un camino para contestar todas las preguntas útiles acerca del problema.? (p. ej. ¿cuáles son las casas de una persona?, ¿a quién le ha comprado esta persona cada casa?, ¿cuándo se realizó la escritura?). Refinar el modelo iterativamente. Es muy difícil que el modelo de objetos nos salga bien a la primera. El desarrollo de software sigue normalmente un proceso iterativo: podemos encontrar errores o mejoras en nuestro modelo de objetos, no sólo mientras lo estamos haciendo, sino al hacer el modelo funcional, el dinámico o incluso en las fases de diseño o implementación. Nuestro objetivo es encontrar los errores lo antes posible, especialmente antes de entregar el software al cliente (pérdida de imagen) y antes de implementar los programas (pérdida de tiempo en codificación y pruebas). Cuanto antes encontremos los errores más sencillo y baratos será corregirlos. Por ello hay que tener especial cuidado en la fase de análisis, revisando el modelo cuantas veces sea necesario, evitando especialmente la disparidad con los modelos dinámico y funcional. 5.8.3. Modelo dinámico. El modelo dinámico muestra el comportamiento de los objetos del modelo de objetos. Podemos empezar estableciendo una lista de los eventos que afectan al sistema, y establecer una o varias trazas de eventos que muestren secuencias normales de estos eventos. Luego estableceremos las secuencias de eventos permitidas para cada clase de objetos, a partir de las cuales podemos realizar los DEs.
5.8.4. Modelo funcional. El modelo funcional muestra cómo se calculan valores, independientemente de cuándo se realizan esos cálculos o de la estructura de los objetos que almacenan esos valores. Lo que muestra el modelo funcional es las relaciones de dependencia de datos y dependencia funcional. Normalmente, la aplicación del paradigma orientado a objetos lleva a una mayor fragmentación del código que los métodos estructurados: los métodos tienen una implementación breve, basada normalmente en la llamada a otros métodos de objetos relacionados. Por este motivo, podemos describir la mayoría de los métodos como primitivas de proceso, o mediante diagramas de interacción, siendo raro el caso en el sea útil describir una determinada operación mediante un DFD. El DE de cada clase contiene la mayor parte de la información necesaria para realizar el modelo funcional. Las acciones y actividades asociadas al disparo de una transición se corresponden con la especificación de la operación que figuran como evento en dicha transición. En caso de que un mismo evento figure en varias transiciones, la especificación contendrá las estructuras de control que permitan distinguir entre los diferentes estados.
reintegro (cantidad) if saldo > 0 then saldo = saldo - cantidad else error("En números rojos"); Si la especificación de una operación contiene invocaciones de operaciones (mediante envío de eventos) a otras clases, puede especificarse mediante diagramas de interacción. Por el contrario, si se trata de una operación exclusivamente interna, o si no se han realizado DE de la clase, la operación se puede especificar mediante una primitiva de proceso. Esta descripción puede hacerse en lenguaje natural, pseudocódigo, por medio de ecuaciones matemáticas o de tablas. La desventaja de hacerlo en lenguaje natural es que la ambigüedad es mayor, pudiendo presentarse problemas a la hora de implementar y que es más difícil comprobar su consistencia. Aunque utilicemos un algoritmo (pseudocódigo) para describir un proceso, el objetivo es especificar qué hace el proceso y no cómo lo hace. La elección definitiva del algoritmo depende de consideraciones adicionales que se establecen en las fases de diseño y de implementación. La especificación de las operaciones debe incluir también cualquier restricción que se deba establecer sobre los datos del sistema, (por ejemplo, precondiciones y postcondiciones de los procesos), así como criterios de optimización que deban tenerse en cuenta en el diseño y la optimización. 5.8.5. Modelo de aplicación. Una vez realizado el modelo del dominio, donde nos habremos centrado fundamentalmente en los objetos del dominio de aplicación, podemos completarlo, convirtiéndolo en un modelo de aplicación. Para ello nos serán de gran utilidad los casos de uso desarrollados junto con la especificación inicial. En primer lugar hemos de determinar los límites del sistema, identificando lo que es parte del mismo y lo que son actores externos. Es posible que algunos objetos que aparecen en el modelo del dominio no formen realmente parte de la aplicación. Para determinarlo, debemos observar si es necesario guardar información sobre estos objetos o si vamos a implementar las operaciones que aparecen en ellos. Por otro lado, el examen de los casos de uso nos permitirá determinar cuál es el acceso necesario para cada clase del dominio. Se pueden construir una o más vistas de cada clase del dominio para proporcionar estas distintas formas de acceso. Estas vistas deben añadirse al modelo de objetos, definiendo la traducción entre objetos del dominio y vistas. Debemos identificar cuales son los eventos que se intercambian entre los actores y el sistema. Los periodos de tiempo entre eventos se definirán como estados. Incluiremos objetos controladores para controlar la secuencia de eventos en cada vista. Esto nos permitirá separar la relación estática entre objetos del dominio y vistas de la dinámica de las vistas. A partir de los casos de uso también podremos determinar cuáles serán los comandos del sistema. Estos comandos serán peticiones que realizan los actores para que el sistema realice una determinada acción. Los comandos del sistema deben añadirse al modelo funcional como operaciones. Si no es posible asignar estas operaciones a ninguna de las clases existentes, la asignaremos a un controlador. Por último debemos determinar las interfaces externas del sistema, incluyendo las interfaces con dispositivos externos, otros sistemas, etc. Es conveniente rodear los objetos de la aplicación de objetos interfaz que los aíslen y eviten dependencias de la aplicación respecto a estos sistemas externos. 5.8.6. Refinar el modelo. Una vez que hemos desarrollado el modelo del sistema, nos quedan aún una serie de cosas por hacer: Completar la lista de operaciones. La primera de ellas es completar la lista de operaciones, dado que normalmente no es posible hacerlo al desarrollar el modelo de objetos, sino que van apareciendo a lo largo del análisis. Podemos clasificar las operaciones de los objetos en los siguientes grupos:
Una vez que hemos completado la lista de operaciones de cada clase, debemos comprobar estas listas con vistas a simplificar operaciones redundantes o a establecer relaciones de generalización entre clases que tengan una serie de operaciones comunes. Comprobar la consistencia entre los modelos. El proceso de análisis no es una actividad secuencial, sino que según vamos desarrollando un modelo surgen modificaciones de los anteriores. Una vez que tenemos los tres modelos hay que comprobar la consistencia, para ver si alguna de estas modificaciones se nos ha pasado por alto. Para comprobar la consistencia nos basaremos en las relaciones entre los modelos de las que ya hemos hablado. Aunque el primer modelo que realicemos sea consistente, rara vez será correcto. Demos revisarlo para ver si se nos ha olvidado algo de lo establecido en la especificación preliminar o si notamos algo extraño. Comprobar el modelo con el cliente. Una vez que nosotros hemos dado el visto bueno al análisis, el cliente debe hacer lo mismo. En primer lugar debemos mostrarle los resultados del análisis para ver si son acordes con sus necesidades, o si hemos cometido algún error o se nos ha pasado por alto algún detalle. También debemos discutir con el las modificaciones que se hayan realizado con respecto a lo establecido en la especificación preliminar, para ver si las suposiciones que hemos hecho son correctas. Deficiencias del análisis estructurado.
Ventajas del análisis orientado a objetos.
Características del enfoque orientado a objetos. Identidad. Los elementos del dominio del problema se organizan en entidades discretas y distinguibles llamadas objetos. Los objetos encapsulan atributos (datos) y operaciones. Cada objeto tiene identidad propia, aunque los valores de sus atributos coincidan con los de otro. Clasificación. Los objetos con propiedades comunes se agrupan en clases. Las clases son abstracciones de características comunes a una serie de objetos. Las clases son arbitrarias: dependen del dominio del problema. Cada uno de los objetos agrupados en una clase se llama instancia. Las instancias de una clase comparten atributos, operaciones y comportamiento pero tienen valores distintos en los atributos. Polimorfismo. Una misma operación puede realizarse de formas distintas en clases distintas. La semántica es común pero la implementación varía en cada clase. La implementación de una operación en una clase se denomina método. Herencia. Es una relación jerárquica entre clases con características comunes. La clase padre define características comunes a todas las hijas. Cada clase hija hereda las características del padre y las amplia o refina. Elementos del modelo de objetos Instancias. Cada uno de los objetos individuales. Clases. Abstracción de objetos con propiedades comunes. Atributos. Datos que caracterizan las instancias de una clase. Operaciones. Funciones que pueden realizar las instancias. Relaciones. Se establecen entre clases. Asociación. Relación de uso en general. Multiplicidad. Número de instancias que intervienen en la relación. Atributos. Algunos atributos pueden depender de la asociación. Calificación. Limita la multiplicidad de las asociaciones. Roles. Indican los papeles de las clases en las relaciones. Restricciones y ordenación. Composición. Relaciones todo/parte. Generalización. Relaciones padre/hijo. Redefinición. Modificación de las propiedades heredadas. Enlaces. Instancias de una relación. Relaciona instancias. Modelo de objetos: consejos prácticos.
Modelo dinámico.
Modelo dinámico: consejos prácticos.
¡ Existen relaciones de composición entre los objetos. ¡ Existe concurrencia interna en el objeto. ¡ Podemos dividir el comportamiento en facetas independientes. ¡ Existen relaciones de herencia entre los objetos.
Modelo de objetos I Pasos a seguir.
Eliminar de la lista de clases candidatas:
Modelo de objetos II Eliminar de la lista de asociaciones candidatas:
Completar las asociaciones indicando:
Modelo dinámico. Pasos a seguir:
Modelo funcional. Pasos a seguir.
Refinar los modelos. Completar la lista de operaciones. Operaciones sobre objetos: creación, destrucción y copia. Operaciones sobre atributos: consulta, actualización acceso a los objetos relacionados clase.atributo devuelve el valor del atributo. clase.atributo := valor asigna valor al atributo. clase.asociación devuelve al objeto asociado. clase.asociación[i] id. para asociaciones múltiples. clase.asociación[calificador] id. para asociaciones calificadas.
Comprobar la consistencia entre los modelos. Comprobar el modelo con el cliente. Ejercicio 1. Realizar el modelo de objetos de un sistema de ficheros, basándose en las siguientes clases identificadas:
Ejercicio 2. El sistema de control de la mayoría de los electrodomésticos consta de los siguientes elementos:
Para arrancar el motor hay que aplicar corriente en los dos contactos (START y RUN) a la vez. Una vez que el motor esta en marcha, basta con mantener la corriente en el contacto RUN. Si el motor se calienta en exceso (TEMP > TMAX), bien porque esté sobrecargado o bien porque no se consiga arrancar, se desconecta automáticamente, y no puede volver a conectarse hasta que se enfríe (TEMP < TMIN).
Ejercicio 3. Realizar el análisis de un editor de diagramas. Los diagramas pueden constar de varias hojas, cada una de ellas formada por cajas de tamaño variable conectadas mediante enlaces. Cada caja irá etiquetada con un nombre. Los enlaces son secuencias de segmentos rectos que conectan dos cajas. Cada segmento viene especificado por dos puntos. Dos segmentos consecutivos comparten un punto en común. Las funciones que debe incorporar el editor son las habituales:
Sólo vamos a realizar el análisis del núcleo del editor, no vamos a modelar el ratón, el teclado, ni la representación de los diagramas en la pantalla. Supondremos que los eventos se envían a los objetos del diagrama desde el entorno del sistema, solicitando la ejecución de alguna operación. Ejemplo de Análisis Orientado a ObjetosRequisitosExplican lo que se desea que haga el sistema, ya sea en lenguaje natural, o en forma de casos de uso a la Jacobson. Ejemplo: Se desea diseñar el software necesario para una red bancaria provista de cajeros automáticos (ATM, automatic teller machines), que serán compartidos por un consorcio de bancos. Cada banco dispone de su propio ordenador, provisto de software propio, que lleva la información sobre sus cuentas y procesa las transacciones que actúan sobre dichas cuentas. A este ordenador están conectadas las estaciones de cajero, que son propiedad del banco y en las que operan cajeros humanos, que pueden crear cuentas e introducir transacciones sobre ellas. Los cajeros automáticos aceptan tarjetas de crédito, interaccionan con el usuario, se comunican con un ordenador central para llevar a cabo las transacciones, entregan dinero en efectivo al usuario e imprimen recibos. El sistema llevará correctamente el registro de las transacciones efectuadas, cumplirá características aceptables de seguridad y manejará correctamente accesos concurrentes a la misma cuenta. El coste de desarrollo de la parte compartida del sistema se dividirá entre los bancos que forman parte del consorcio en función del número de clientes provistos de tarjetas de crédito. Expresar los requisitos como Casos de UsoPreparar escenarios detallados. Primero los normales. Después se añaden los problemas que pueden surgir. En el ejemplo de los cajeros automáticos:
Modelo de objetosConsta de los siguientes pasos:
Identificar objetos y clasesConsta de los siguientes pasos:
Resultado: Preparar diccionario de clases En el ejemplo de los cajeros automáticos:
Resultado. Del análisis anterior, resultan seleccionadas las siguientes clases (11): Cajero automático, Consorcio de bancos, Banco, Ordenador del banco, Cuenta bancaria, Transacción, Estaciones de cajero, Cajero humano, Tarjeta de crédito, Ordenador central, Cliente. El diccionario de clases contiene la definición detallada de todas estas clases en lenguaje natural. Ejemplo:
Identificar y depurar relacionesConsta de los siguientes pasos:
En el ejemplo de los cajeros automáticos:
Identificar atributos de objetos y relacionesConsta de los siguientes pasos:
En el ejemplo de los cajeros automáticos:
Añadir herenciaIntroducimos clases nuevas (virtuales) que contienen información común a dos o más clases preexistentes. Procurar evitar la herencia múltiple, a menos que sea estrictamente necesaria. Resultado: Primer diagrama de clases En el ejemplo de los cajeros automáticos:
Comprobar los casos de uso (iterar)Para localizar fallos que deben corregirse fijarse en:
En el ejemplo de los cajeros automáticos:
ModularizarAgrupar clases en módulos. En el ejemplo de los cajeros automáticos. Posibles módulos:
Añadir y simplificar métodos
Modelo dinámicoConsta de los siguientes pasos:
Identificar sucesosLos sucesos se extraen de los casos de uso (escenarios). Pueden ser de los siguientes tipos:
Resultados: Diagramas de secuencia (trazas de eventos) y diagramas de colaboración (diagramas de flujo de eventos). Los casos de uso (escenarios) se convierten en diagramas de secuencia. Estas se compactan en diagramas de colaboración. En el ejemplo de los cajeros automáticos: El cliente introduce la contraseña define un evento de entrada que el objeto Cliente envía al objeto Cajero automático. El cajero automático entrega el dinero al cliente es un evento que el objeto Cajero automático envía al objeto Cliente. Agrupar los eventos equivalentes: El cliente introduce la contraseña es el mismo evento independientemente de la contraseña introducida. El cajero automático entrega el dinero al cliente es el mismo evento independientemente de la cantidad entregada. No agrupar los eventos no equivalentes: El banco autoriza la transacción es distinto evento que El banco rechaza la transacción. Construir diagramas de estadosUno por clase. En el ejemplo de los cajeros automáticos centrarse en las clases dinámicas, que cambian de estado:
No hace falta construir diagramas de estado de las clases pasivas, que no cambian de estado de modo significativo:
Tampoco hace falta considerar a fondo los objetos externos, que no forman parte del sistema informático:
Añadir métodosLos eventos son métodos. Es preciso decidir de qué clase de objetos. Las acciones y actividades realizadas en los estados son métodos. Modelo funcionalConsta de los siguientes pasos:
Identificar valores de entrada/salidaSon los que pasan información desde los objetos externos al sistema de software propiamente dicho. En el ejemplo de los cajeros automáticos son objetos externos:
Los valores de entrada/salida serán:
Construir diagramas de flujo de actividadRelacionan los valores de entrada con los de salida. Suele dividirse en varias capas o niveles. En el ejemplo de los cajeros automáticos:
Describir funcionesDescripción de cada una de las funciones de nivel mínimo que aparecen en los diagramas de flujo de actividad. La descripción puede ser:
En el ejemplo de los cajeros automáticos: Descripción de la función "actualizar cuenta": actualizar cuenta (cuenta, cantidad, tipo de transacción)
-> efectivo, recibo, mensaje
Si es una retirada de efectivo:
Si la cantidad a retirar excede el saldo,
rechazar la transacción y no entregar dinero
En caso contrario:
Restar la cantidad del saldo y entregar dinero
Si es un depósito:
Aumentar el saldo de la cuenta y no entragar dinero
Si es una petición de información:
Escribir saldo y no entregar dinero
En cualquier caso:
El recibo incluye:
- número del cajero automático
- fecha y hora
- número de la cuenta
- tipo de transacción
- cantidad movida
- nuevo saldo
Identificar restricciones y dependencias funcionales entre objetosEn el ejemplo de los cajeros automáticos:
Definir criterios de optimización (iterar)En el ejemplo de los cajeros automáticos:
Añadir métodosLas funciones del modelo funcional pueden ser simples transferencias de información, o corresponder a un método de algún objeto (operaciones interesantes). En este caso hay que asignarlos y añadirlos al modelo de objetos. En el ejemplo de los cajeros automáticos, son interesantes:
Se añadirán otros métodos, no relacionados con ninguna de las cuestiones anteriores, procedentes de nuestro conocimiento del tema:
|
|