Utilizar mecanismos y patrones de diseño
Utilice los mecanismos y los patrones de diseño que se adapten a la clase o posibilidad que está diseñando, de acuerdo
con las directrices de diseño del proyecto.
La incorporación de un patrón y/o un mecanismo significa realizar de forma eficaz muchos de los pasos posteriores en
esta tarea (añadir nuevas clases, operaciones, atributos y relaciones), de acuerdo con las reglas definidas por el
patrón o mecanismo.
Tenga en cuenta que los patrones y los mecanismos se incorporan normalmente a medida que evoluciona el diseño, no sólo
como el primer paso en esta tarea. También se aplican a menudo entre un conjunto de clases, en lugar de sólo a una
clase.
|
Crear clases de diseño inicial
Cree una o varias clases de diseño inicial para la clase de análisis proporcionada como entrada a esta tarea y asigne
dependencias de rastreo. Las clases de diseño creadas en este paso se perfeccionarán, ajustarán, dividirán o fusionarán
en los pasos posteriores cuando se les asignen distintas propiedades de diseño como, por ejemplo, operaciones, métodos
y una máquina de estado, que describen cómo se diseña la clase de análisis.
Dependiendo del tipo de clase de análisis (límite, entidad o control) que se está diseñando, existen estrategias
específicas que puede utilizar para crear clases de diseño inicial.
Las clases de límite representan interfaces con los usuarios u otros sistemas.
Normalmente, las clases de límite que representan interfaces con otros sistemas se modelan como subsistemas, ya que a
menudo tienen un comportamiento interno complejo. Si el comportamiento de la interfaz es sencillo (por ejemplo, si
actúa sólo como paso a través a una API existente del sistema externo), puede elegir representar la interfaz con una o
más clases de diseño. Si elige esta ruta, utilice una única clase de diseño por protocolo, interfaz o API, y tenga en
cuenta los requisitos especiales sobre los estándares que ha utilizado en los requisitos especiales de la clase.
Las clases de límite que representan interfaces con usuarios generalmente siguen la regla de una clase de límite para
cada ventana, o una para cada formulario, en la interfaz de usuario. Por lo tanto, las responsabilidades de las clases
de límite pueden tener un nivel bastante alto, y se deben perfeccionar y detallar en este paso. Los modelos o
prototipos adicionales de la interfaz de usuario pueden ser otra fuente de entrada a considerar en este paso.
El diseño de las clases de límite depende de las herramientas de desarrollo de la interfaz de usuario (UI) que hay
disponibles para el proyecto. Utilizando la tecnología actual, lo normal es que la UI se construya directamente en la
herramienta de desarrollo de forma visual. Esto crea automáticamente clases de UI que se deben relacionar con el diseño
de las clases de control y entidad. Si el entorno de desarrollo de UI crea automáticamente las clases de soporte que
necesita para implementar la UI, no es necesario considerarlas en el diseño. Sólo es necesario diseñar aquellos
elementos que no crea el entorno de desarrollo automáticamente.
Durante el análisis, las clases de entidad representan unidades de información manipuladas. A menudo son pasivas y
persistentes, y se pueden identificar y asociar con el mecanismo de análisis de la persistencia. Los detalles del
diseño de un mecanismo de persistencia basado en base de datos se describen en Tarea: Diseño de
base de datos. Las consideraciones del rendimiento pueden forzar la refactorización de las clases persistentes,
provocando cambios en el modelo de diseño que se describen conjuntamente en Rol:
Diseñador de base de datos y Rol: Diseñador.
Puede encontrar más adelante una descripción más amplia de los temas de diseño de las clases persistentes en la
cabecera Identificar clases persistentes.
Un objeto de control es responsable de gestionar el flujo de un caso de uso y, por lo tanto, coordina la mayoría de sus
acciones; los objetos de control encapsulan la lógica que no está particularmente relacionada con los aspectos de la
interfaz de usuario (objetos de límite) o los aspectos de la ingeniería de datos (objetos de entidad). Esta lógica se
denomina también lógica de aplicaciones o la lógica empresarial.
Tenga en cuenta los aspectos siguientes cuando diseñe clases de control:
-
Complejidad: Puede manejar el comportamiento de coordinación o control que no es complicado utilizando
clases de límite o entidad. No obstante, a medida que aumenta la complejidad de la aplicación, este enfoque
presenta algunos inconvenientes significativos como, por ejemplo:
-
el comportamiento de coordinación del caso de uso se incorpora en la UI, lo que hace que sea más difícil
cambiar el sistema
-
la misma UI no se puede utilizar en realizaciones de casos de uso diferentes sin dificultad
-
la UI se sobrecarga de funcionalidad adicional, lo que reduce su rendimiento
-
los objetos de entidad se pueden sobrecargar con el comportamiento específico del caso de uso , lo que reduce
su generalidad
Para evitar estos problemas, se introducen las clases de control para proporcionar un comportamiento relacionado
con la coordinación de flujos de sucesos.
-
Probabilidad de cambio: Si la probabilidad de cambiar flujos de sucesos es menor o el coste es
insignificante, el gasto y la complejidad adicionales de añadir clases de control puede no estar justificada.
-
Distribución y rendimiento: La necesidad de ejecutar componentes de la aplicación en distintos nodos, o en
distintos espacios de proceso, introduce la necesidad de especializar los elementos del modelo de diseño. Esta
especialización se consigue normalmente añadiendo objetos de control y distribuyendo el comportamiento desde las
clases de límite y entidad a las clases de control. De esta forma, las clases de límite migran a proporcionar sólo
servicios de UI, las clases de entidad pasan a proporcionar sólo servicios de datos y las clases de control
proporcionan el resto.
-
Gestión de transacciones: La gestión de transacciones es una actividad de coordinación clásica. Sin una
infraestructura para manejar la gestión de transacciones, una o varias clases de gestor de transacciones
tendrán que interactuar para garantizar que se mantiene la integridad de las transacciones.
En los últimos dos casos, si la clase de control representa una hebra de control independiente, puede que sea más
adecuado utilizar una clase activa para modelar la hebra de control. En un sistema en tiempo real, el uso de Producto de trabajo: Cápsulas es el enfoque de modelado preferido.
|
Identificar clases persistentes
Las clases que deben almacenar su estado en un medio permanente se conocen como persistentes. La necesidad de almacenar
su estado puede ser para el registro permanente de información de las clases, como copia de seguridad en el caso de una
anomalía del sistema, o para el intercambio de información. Una clase persistente puede tener instancias persistentes y
transitorias; el etiquetado de una clase como persistente significa sólo que algunas instancias de la clase puede que
tengan que ser persistentes.
Incorpore los mecanismos de diseño correspondientes a los mecanismos de persistencia encontrados durante el análisis.
Por ejemplo, dependiendo de las necesidades de la clase, el mecanismo de análisis de persistencia puede realizarse
mediante uno de estos mecanismos de diseño:
-
Almacenamiento en memoria
-
Tarjeta Flash
-
Archivo binario
-
Sistema de gestión de bases de datos (DBMS)
Los objetos persistentes puede que no se deriven sólo de clases de entidad; los objetos persistentes también pueden ser
necesarios para manejar requisitos no funcionales en general. Algunos ejemplos son los objetos persistentes necesarios
para mantener la información relevante para el control de procesos o para mantener información de estado entre
transacciones.
La identificación de clases persistentes permite notificar al Rol:
Diseñador de base de datos que la clase necesita especial atención a las características de almacenamiento físico.
También notifica al Rol: Arquitecto de software que la clase debe ser persistente y al Rol: Diseñador responsable del mecanismo de persistencia que las
instancias de la clase deben convertirse en persistentes.
Debido a la necesidad de una estrategia de persistencia coordinada, el Rol:
Diseñador de base de datos es el responsable de correlacionar las clases persistentes en la base de datos,
utilizando una infraestructura de persistencia. Si el proyecto desarrolla una infraestructura de persistencia, el
desarrollador de la infraestructura también será responsable de entender los requisitos de persistencia de las clases
de diseño. Para proporcionar a estas personas la información que necesitan, es suficiente en este punto indicar que la
clase es persistente o, más concretamente, que las instancias de la clase son persistentes.
|
Definir la visibilidad de la clase
Para cada clase, determine la visibilidad de la clase en el paquete en el que reside. Se puede hacer referencia a una
clase pública fuera del paquete que la contiene. Sólo se puede hacer referencia a una clase privada (o
una cuya visibilidad es implementación) desde las clases dentro del mismo paquete.
|
Definir operaciones
Para identificar operaciones en las clases de diseño:
-
Estudie las responsabilidades de cada clase de análisis correspondiente, creando una operación para cada
responsabilidad. Utilice la descripción de la responsabilidad como la descripción inicial de la operación.
-
Estudie las realizaciones de los casos de uso en la clase participates para ver cómo utilizan las
operaciones. Amplíe las operaciones, para cada realización de caso de uso , y perfecciones las operaciones, sus
descripciones, tipos de retorno y parámetros. Los requisitos de cada realización de caso de uso pertenecientes a
las clases se describen textualmente en el Flujo de sucesos de la realización del caso de uso .
-
Estudie el caso de uso Requisitos especiales para asegurarse de que no omite los requisitos implícitos en la
operación que allí se indica.
Las operaciones son necesarias para dar soporte a los mensajes que aparecen en los diagramas de secuencia, ya que los
scripts -especificaciones de mensajes temporales que no se han asignado todavía a las operaciones- describen el
comportamiento que se espera de la clase. En la Figura 1 se muestra un ejemplo de un diagrama de secuencia.
Figura 1: Los mensajes constituyen la base para identificar operaciones
Las realizaciones de los casos de uso no pueden proporcionar suficiente información para identificar todas las
operaciones. Para encontrar las operaciones restantes, tenga en cuenta lo siguiente:
-
¿Hay alguna forma de inicializar una nueva instancia de la clase, incluido conectarla a instancias de otras clases
con las que está asociada?
-
¿Es necesario probar si dos instancias de la clase son iguales?
-
¿Es necesario crear una copia de una instancia de la clase?
-
¿Hay operaciones necesarias en la clase por los mecanismos que utilizan? Por ejemplo, un mecanismo de
recopilación de basura puede necesitar que un objeto pueda eliminar todas sus referencias a los demás
objetos, para que se puedan liberar los recursos no utilizados.
No defina operaciones que sólo obtengan y establezcan los valores de atributos públicos (consulte Definir atributos y Definir asociaciones).
Normalmente, se generan mediante recursos de generación de código y no es necesario definirlas de forma explícita.
Utilice convenios de denominación para el lenguaje de implementación cuando denomine operaciones, tipos de retorno, y
parámetros y sus tipos. Se describen en las Directrices específicas del proyecto.
Para cada operación, debe definir lo siguiente:
-
El nombre de operación: Proporcione un nombre abreviado y descriptivo del resultado de la operación.
-
Los nombres de las operaciones deben seguir la sintaxis del lenguaje de implementación. Por ejemplo:
find_location será aceptable para C++ o Visual Basic, pero no para Smalltalk (donde no se utilizan
subrayados); un nombre más adecuado será findLocation.
-
Evite nombres que impliquen cómo se realiza la operación. Por ejemplo, Employee.wages() es mejor que
Employee.calculateWages(), ya que el último implica que se realiza un cálculo. La operación puede
devolver simplemente un valor en una base de datos.
-
El nombre de una operación debe mostrar claramente su objetivo. Evite nombres poco específicos como, por
ejemplo, getData, que no son descriptivos del resultado que devuelven. Utilice un nombre que muestre
exactamente qué se espera como, por ejemplo, getAddress. O mejor aún, haga que el nombre de la
operación sea el nombre de la propiedad que se devuelve o se establece. Si tiene un parámetro, establece la
propiedad. Si no tiene ningún parámetro, obtiene la propiedad. Por ejemplo: la operación address
devuelve la dirección de un Cliente, mientras que address(aString) establece o cambia la
dirección del Cliente. La naturaleza de obtener y establecer de la operación es
implícita desde la firma de la operación.
-
Las operaciones que son conceptualmente idénticas deberían tener el mismo nombre, aunque las definan clases
diferentes, se implementen de forma completamente distinta o tengan un número de parámetros diferente. Por
ejemplo, una operación que crea un objeto debería tener el mismo nombre en todas las clases.
-
Si hay operaciones en varias clases que tienen la misma firma, la operación debe devolver el mismo tipo de
resultado adecuado para el objeto receptor. Este es un ejemplo del concepto de polimorfismo, que
establece que objetos diferentes deben responder al mismo mensaje de forma parecida. Por ejemplo: la
operación name debe devolver el nombre del objeto, independientemente de cómo se almacene o se
derive el nombre. Si se sigue este principio, el modelo es más fácil de entender.
-
El tipo de retorno: El tipo de retorno debe ser la clase de objeto que devuelve la operación.
-
Una breve descripción: Aunque proporcione un nombre de operación significativo, a menudo sólo es vagamente
útil cuando se intenta entender qué hace la operación. Proporcione una breve descripción de la operación formada
por un par de frases, escritas desde la perspectiva del usuario de la operación.
-
Los parámetros: Para cada parámetro, cree un nombre descriptivo abreviado, decida su clase y proporcione una
breve descripción. Cuando especifique parámetros, recuerde que cuanto menor sea el número de parámetros, mayor es
la capacidad de reutilización. Un número pequeño de parámetros hace que la operación sea más fácil de entender y,
por lo tanto, hay una mayor probabilidad de encontrar operaciones parecidas. Puede que tenga que dividir una
operación con muchos parámetros en varias operaciones. La operación debe ser comprensible para aquellos que deseen
utilizarla. La breve descripción debe incluir:
-
el significado de los parámetros, si no es aparente en el nombre
-
si el parámetro se pasa por valor o por referencia
-
parámetros que deben tener valores proporcionados
-
parámetros que pueden ser opcionales y los valores por omisión, si no se proporciona ningún valor
-
rangos válidos para los parámetros, si es aplicable
-
qué se hace en la operación
-
qué parámetros por referencia modifica la operación
Una vez definidas las operaciones, complete los diagramas de secuencia con información sobre qué operaciones se invocan
para cada mensaje.
Consulte el apartado Directriz de producto de trabajo: Clase de diseño para obtener más información.
Para cada operación, identifique la visibilidad de exportación de la operación a partir de estas opciones:
-
Pública: la operación es visible para modelar elementos diferentes de la propia clase.
-
Implementación: la operación es visible sólo dentro de la misma clase.
-
Protegida: la operación es visible sólo para la clase, sus subclases, o para amigos de la clase
(dependientes del lenguaje).
-
Privada: la operación sólo es visible para la clase y los amigos de la clase.
Elija la visibilidad más restringida posible que pueda lograr los objetivos de la operación. Para ello, observe los
diagramas de secuencia y, para cada mensaje, determine si el mensaje proviene de una clase fuera del paquete del
receptor (requiere visibilidad pública), del interior del paquete (requiere visibilidad de
implementación), de una subclase (requiere visibilidad protegida), o de la propia clase o un amigo
(requiere visibilidad privada).
En la mayoría de los casos, las operaciones son operaciones de instancias; esto es, se ejecutan en instancias de la
clase. No obstante, hay casos en los que una operación se aplica a todas las instancias de la clase y, por lo tanto, es
una operación de ámbito de clase. El receptor de la operación de clase es una instancia de una metaclase
-la descripción de la propia clase- en lugar de una instancia específica de la clase. Los ejemplos de las operaciones
de clase incluyen mensajes que crean nuevas instancias, que devuelven todas las instancias de una clase.
La serie de la operación se subraya para denotar una operación de ámbito de clase.
|
Definir métodos
Un método especifica la implementación de una operación. En muchos casos en los que el comportamiento necesario para la
operación está suficientemente definido por el nombre de operación, la descripción y los parámetros, los métodos se
implementan directamente en el lenguaje de programación. Cuando la implementación de una operación requiere el uso de
un algoritmo específico o más información de la que se presenta en la descripción de la operación, se necesita una
descripción de método aparte. El método describe cómo funciona la operación, no sólo lo que hace.
El método debe describir cómo:
-
se implementarán las operaciones
-
se implementarán los atributos y se utilizarán para implementar operaciones
-
se implementarán las relaciones y se utilizarán para implementar operaciones
Los requisitos variarán de un caso a otro, aunque las especificaciones de método de una clase deben indicar siempre:
-
qué se hará de acuerdo con los requisitos
-
qué otros objetos se utilizarán (y sus operaciones)
Otros requisitos más específicos hacen referencia a:
-
cómo se implementarán los parámetros
-
qué algoritmos especiales, si hay alguno, se utilizarán
Los diagramas de secuencia constituyen una fuente importante de información. A partir de ellos, se deduce qué
operaciones se utilizarán en otros objetos cuando se ejecute una operación. La especificación de qué operaciones se
utilizarán en otros objetos es necesaria para la completa implementación de una operación. Por lo tanto, la producción
de una especificación de método completa requiere que identifique las operaciones de los objetos implicados e
inspeccione los diagramas de secuencia correspondientes.
|
Definir estados
Para algunas operaciones, el comportamiento de la operación depende del estado en el que está el objeto receptor. Una
máquina de estado es una herramienta que describe los estados que puede asumir un objeto y los sucesos que hacen que el
objeto pase de un estado a otro (consulte Técnica:
Diagrama de gráfico de estados). Las máquinas de estado son muy útiles para describir clases activas. La
utilización de máquinas de estado es particularmente importante para definir el comportamiento de Producto de trabajo: Cápsulas.
En la Figura 2 se muestra un ejemplo de una máquina de estado simple.
Figura 2: Un diagrama de gráfico de estados simple de un dispensador de combustible
Cada suceso de transición de estado se puede asociar con una operación. Dependiendo del estado del objeto, la operación
puede tener un comportamiento diferente y los sucesos de transición describen este cambio de comportamiento.
La descripción de método de la operación asociada se debe actualizar con la información específica del estado,
indicando para cada estado relevante qué debe hacer la operación. Los estados se representan a menudo utilizando
atributos; los diagramas de gráfico de estados sirven como entrada en el paso de identificación del atributo.
Para obtener más información, consulte Técnica:
Diagrama de gráfico de estados.
|
Definir atributos
Durante la definición de los métodos y la identificación de los estados, se identifican los atributos que
necesita la clase para llevar a cabo sus operaciones. Los atributos proporcionan almacenamiento de información para la
instancia de clase y a menudo se utilizan para representar el estado de la instancia de clase. La información mantenida
por la clase se mantiene mediante sus atributos. Para cada atributo, defina:
-
el nombre, que debe seguir los convenios de denominación del lenguaje de implementación y el proyecto
-
el tipo, que será un tipo de datos elemental soportado por el lenguaje de implementación
-
el valor inicial o por omisión con el que se inicializa cuando se crean nuevas instancias de la clase
-
la visibilidad, que tomará uno de los valores siguientes:
-
Pública: el atributo es visible desde dentro y fuera del paquete que contiene la clase
-
Protegida: el atributo es visible sólo para la clase, sus subclases, o para amigos de la clase
(dependientes del lenguaje)
-
Privada: el atributo sólo es visible para la clase o los amigos de la clase
-
Implementación: el atributo es visible sólo para la clase
-
las clases persistentes, tanto si el atributo es persistente (valor por omisión) como si es transitorio.
Aunque la clase sea persistente, no todos los atributos de la clase tienen que ser persistentes.
Compruebe que todos los atributos son necesarios. Los atributos deben estar justificados; es fácil que los atributos se
añadan al principio del proceso y sobrevivan cuando ya no son necesarios debido a falta de previsión. Los atributos
adicionales, multiplicados por miles o millones de instancias, pueden tener un efecto negativo en el rendimiento y los
requisitos de almacenamiento de un sistema.
Consulte el apartado Atributos en Directriz de producto
de trabajo: Clase de diseño para obtener más información sobre los atributos.
|
Definir dependencias
En los casos en los que se necesite la comunicación entre objetos, hágase estas preguntas:
-
¿La referencia al receptor se ha pasado como parámetro a la operación? En caso afirmativo, establezca una
dependencia entre las clases de emisor y receptor en un diagrama de clase que contenga las dos clases.
Asimismo, si se utiliza el formato de diagrama de comunicación para las interacciones, califique la
visibilidad del enlace y establézcala como parámetro.
-
¿El receptor es global? En caso afirmativo, establezca una dependencia entre las clases de emisor y receptor
en un diagrama de clase que contenga las dos clases. Asimismo, si se utiliza el formato de diagrama de
comunicación para las interacciones, califique la visibilidad del enlace y establézcala como global.
-
¿El receptor es un objeto temporal creado y destruido durante la operación? En caso afirmativo, establezca una
dependencia entre las clases de emisor y receptor en un diagrama de clase que contenga las dos clases.
Asimismo, si se utiliza el formato de diagrama de comunicación para las interacciones, califique la
visibilidad del enlace y establézcala como local.
Tenga en cuenta que los enlaces modelados de esta forma son enlaces transitorios, que sólo existen un tiempo limitado
en el contexto específico de la colaboración; en este sentido, son instancias del rol de asociación en la colaboración.
No obstante, la relación en un modelo de clase (es decir, independiente del contexto) debe ser una dependencia, tal
como se ha indicado anteriormente. Como se describe en [RUM98], en la
definición de enlace transitorio: "Todos estos enlaces se pueden modelar como asociaciones, pero las condiciones
de las asociaciones se deben establecer de forma amplia, y pierden mucha precisión en la restricción de combinaciones
de objetos". En esta situación, el modelado de una dependencia es menos importante que el modelado de la relación en la
colaboración, ya que la dependencia no describe la relación completamente; sólo que existe.
|
Definir asociaciones
Las asociaciones proporcionan el mecanismo para que los objetos se comuniquen entre ellos. Proporcionan a los objetos
un conducto por el que pueden fluir los mensajes. También documentan las dependencias entre clases, resaltando que los
cambios en una clase se pueden sentir en muchas otras clases.
Examine las descripciones de método de cada operación para entender cómo se comunican y colaboran las instancias
de la clase con otros objetos. Para enviar un mensaje a otro objeto, un objeto debe tener una referencia al receptor
del mensaje. Un diagrama de comunicación (una representación alternativa de un diagrama de secuencia) mostrará la
comunicación del objeto en términos de enlaces, tal como se muestra en la Figura 3.
Figura 3: Un ejemplo de un diagrama de comunicación
Los restantes mensajes utilizan la asociación o la agregación para especificar la relación entre las
instancias de dos clases que se comunican. Consulte los apartados Técnica: Asociación
y Técnica: Agregación para obtener información sobre cómo elegir la representación
adecuada. Para estas dos asociaciones, establezca la visibilidad del enlace como campo en los diagramas de
comunicación. Otras tareas posibles son:
-
Establezca la navegabilidad de las asociaciones y las agregaciones. Para ello, considere que las navegabilidades
son necesarias en las creaciones de instancias de enlaces en los diagramas de interacción. Como la navegabilidad es
true por omisión, sólo tiene que encontrar asociaciones (y agregaciones) allí donde todos los roles de
enlace opuestos de todos los objetos de una clase en la asociación no requieran navegabilidad. En estos casos,
establezca la navegabilidad en false en el rol de la clase.
-
Si hay atributos en la propia asociación (representados por clases de asociación), cree una clase de diseño para
representar la clase de asociación, con los atributos correspondientes. Interponga esta clase entre las otras dos
clases y establezca asociaciones con la multiplicidad adecuada entre la clase de asociación y las otras dos clases.
-
Especifique si los extremos de la asociación se deben ordenar o no; este es el caso cuando los
objetos asociados con un objeto en el otro extremo de la asociación tienen un orden que se debe conservar.
-
Si sólo se hace referencia a la clase asociada (o agregada) en la clase actual, considere si se debe anidar la
clase. Las ventajas de anidar las clases son una mensajería más rápida y un modelo de diseño más sencillo. Las
desventajas son la asignación estática del espacio de la clase anidada, independientemente de si hay instancias de
la clase anidada, la falta de identidad de los objetos independiente de la clase inclusiva, o la incapacidad de
hacer referencia a instancias de clases anidadas desde fuera de la clase inclusiva.
Las asociaciones y las agregaciones se definen mejor en un diagrama de clase que describa las clases asociadas. El
diagrama de clase debe ser propiedad del paquete que contiene las clases asociadas. En la Figura 4 se muestra un
ejemplo de un diagrama de clase donde se describen las asociaciones y las agregaciones.
Figura 4: Ejemplo de un diagrama de clase donde se muestran las asociaciones, agregaciones y generalizaciones entre
clases
Las asociaciones de suscripción entre clases de análisis se utilizan para identificar las dependencias de
sucesos entre clases. En el modelo de diseño, debe manejar estas dependencias de sucesos de forma explícita. Para ello,
utilice las infraestructuras de manejador de sucesos disponibles, o bien diseñe y cree su propia infraestructura de
manejador de sucesos. En algunos lenguajes de programación como, por ejemplo, Visual Basic, es sencillo: el usuario
declara, detecta y maneja los sucesos correspondientes. En otros lenguajes, deberá utilizar una biblioteca adicional de
funciones reutilizables para manejar las suscripciones y los sucesos. Si la funcionalidad no se puede adquirir, deberá
diseñarla y construirla. Consulte también Técnica:
Asociación de suscripción.
|
Definir la estructura interna
Algunas clases pueden representar abstracciones complejas y pueden tener una estructura compleja. Cuando se modela una
clase, el diseñador puede desear representar los elementos de participación interna y sus relaciones, para asegurarse
de que el implementador implementa las colaboraciones que se producen dentro de la clase como corresponde.
En UML 2.0, las clases se definen como clases
estructuradas, con la posibilidad de tener una estructura interna y puertos. Las clases se pueden descomponer en
recopilaciones de componentes conectados que se pueden a descomponer, a su vez. Una clase se puede encapsular forzando
las comunicaciones desde el exterior para pasar a través de los puertos que obedecen a las interfaces declaradas.
Cuando encuentre una clase compleja con una estructura compleja, cree un diagrama de estructura compuesta para esa
clase. Modele las partes que ejecutarán los roles de ese comportamiento de clase. Establezca cómo se 'conectan' las
partes entre sí utilizando conectores. Utilice los puertos con interfaces declaradas si desea permitir que distintos
clientes de esa clase accedan a partes específicas del comportamiento que ofrece la clase. Utilice también los puertos
para aislar completamente las partes internas de la clase de su entorno.
Para obtener más información sobre este tema y ejemplos de diagramas de estructura compuesta, consulte Concepto: Clase estructurada.
|
Definir generalizaciones
Las clases se pueden organizar en una jerarquía de generalizaciones para reflejar el comportamiento común y la
estructura común. Se puede definir una superclase común, desde la que las subclases pueden heredar el
comportamiento y la estructura. La generalización es un convenio de notación que permite definir una estructura y un
comportamiento común en un lugar, y utilizarlos donde se encuentre un comportamiento y una estructura repetidos.
Consulte el apartado Técnica:
Generalización para obtener más información sobre las relaciones de generalización.
Cuando encuentre una generalización, cree una superclase común que contenga los atributos, las asociaciones, las
agregaciones y las operaciones comunes. Elimine el comportamiento común de las clases que serán subclases de la
superclase común. Defina una relación de generalización desde la subclase a la superclase.
|
Resolver colisiones de casos de uso
El objetivo de este paso es evitar los conflictos de concurrencia provocados cuando dos o más casos de uso pueden
acceder simultáneamente a instancias de la clase de diseño, probablemente de forma incoherente.
Una de las dificultades de continuar "caso de uso " a "caso de uso " a través del proceso de diseño es que dos o más
casos de uso pueden intentar invocar operaciones simultáneamente en objetos de diseño de forma que entren en conflicto.
En estos casos, los conflictos de concurrencia se deben identificar y resolver explícitamente.
Si se utiliza la mensajería síncrona, la ejecución de una operación bloqueará las posteriores llamadas a los objetos
hasta que finalice la operación. La mensajería síncrona implica un orden de "primero en llegar, primero en servirse"
para el proceso de mensajes. Esto puede resolver el conflicto de concurrencia, especialmente en aquellos casos en los
que todos los mensajes tienen la misma prioridad o en los que cada mensaje se ejecuta dentro de la misma hebra de
ejecución. En los casos donde distintas hebras de ejecución (representadas por clases activas) pueden acceder a un
objeto, se deben utilizar mecanismos explícitos para impedir o resolver el conflicto de concurrencia.
En sistemas en tiempo real en los que las hebras están representadas por Producto de
trabajo: Cápsulas, este problema todavía tiene que resolverse para el acceso concurrente múltiple a objetos
pasivos, mientras que las cápsulas proporcionan un mecanismo de cola y fuerzan la semántica de ejecución hasta el final
para manejar el acceso concurrente. Una solución recomendada es encapsular los objetos pasivos en cápsulas, lo que
evita el problema del acceso concurrente mediante la semántica de la propia cápsula.
Varias hebras de ejecución diferentes pueden invocar simultáneamente distintas operaciones en el mismo objeto sin que
se produzca ningún conflicto de concurrencia; por ejemplo, se pueden modificar concurrentemente el nombre y la
dirección de un cliente sin ningún conflicto. Es sólo cuando dos hebras de ejecución diferentes intentan modificar la
misma propiedad del objeto cuando se produce el conflicto.
Para cada objeto al que pueden acceder concurrentemente varias hebras de ejecución diferentes, identifique las
secciones de código que se deben proteger del acceso simultáneo. Al principio de la fase de elaboración, no se podrán
identificar segmentos de código específicos; las operaciones que se deben proteger bastarán. Posteriormente, seleccione
o diseñe los mecanismos de control de acceso adecuados para impedir los conflictos de acceso simultáneo. Ejemplos de
estos mecanismos son la cola de mensajes para serializar el acceso, el uso de semáforos o señales para permitir el
acceso a una hebra cada vez, u otras variantes de mecanismos de bloqueo. La elección del mecanismo tiende a depender
mucho de la implementación y normalmente varía con el lenguaje de programación y el entorno operativo. Consulte las Directrices específicas del proyecto para obtener ayuda en la
selección de mecanismos de concurrencia.
|
Manejar requisitos no funcionales en general
Las clases de diseño están perfeccionadas para manejar requisitos no funcionales generales. Una entrada importante para
este paso son los requisitos no funcionales en una clase de análisis que puede tener ya establecidos sus requisitos
especiales y responsabilidades. Estos requisitos se especifican a menudo en términos de qué mecanismos de arquitectura
(análisis) se necesitan para realizar la clase; en este paso, la clase se perfecciona para incorporar los mecanismos de
diseño correspondientes a estos mecanismos de análisis.
El arquitecto de software identifica y caracteriza los mecanismos de diseño disponibles. Para cada mecanismo de diseño
necesario, califique tantas características como sea posible, proporcionando rangos cuando corresponda. Consulte el
apartado Tarea: Identificar los mecanismos de diseño, Concepto: Mecanismos de análisis y Concepto: Mecanismos de implementación y diseño para obtener más información sobre
los mecanismos de diseño.
Existen varios mecanismos y directrices de diseño generales que se deben tener en cuenta cuando se diseñan las clases,
por ejemplo, cómo...
-
utilizar los productos y los componentes existentes
-
adaptarse al lenguaje de programación
-
distribuir objetos
-
conseguir un rendimiento aceptable
-
alcanzar determinados niveles de seguridad
-
manejar los errores
|
Evaluar los resultados
Compruebe el modelo de diseño en esta fase para verificar que el trabajo sigue la dirección correcta. No es necesario
revisar el modelo al detalle, pero debe tener en cuenta las siguientes listas de comprobación:
|
|