Atividade:
| ||||||||||||||
Finalidade
|
|
| Passos | |
| Artefatos Informados: | Artefatos Resultantes: |
| Papel: Designer | |
| Mentor de Ferramentas: | |
| Detalhamentos do Fluxo de Trabalho: |
O comportamento de um sistema pode ser descrito através de técnicas variadas - colaborações ou interações. Esta atividade descreve o uso de interações, especificamente diagramas de seqüência, para descrever o comportamento do sistema. Os diagramas de seqüência são mais úteis quando o comportamento do sistema ou do subsistema puder ser descrito basicamente pelo serviço de mensagens síncronas. O sistema de mensagens assíncronas, principalmente em sistemas controlados por eventos, é muitas vezes descrito mais facilmente em termos de máquinas de estado e colaborações, o que permite definir possíveis interações entre objetos de maneira compacta.
As mensagens assíncronas desempenham um importante papel em sistemas de tempo real ou reativos e são usadas para a comunicação entre instâncias do Artefato: Cápsulas.
Para cada realização de casos de uso, ilustre as interações entre seus objetos de design participantes, criando um ou mais diagramas de seqüência. Versões iniciais desses diagramas poderão ter sido criadas durante a Atividade: Análise de Caso de Uso. Se tiverem sido criadas no Artefato: Modelo de Análise, elas precisarão ser transpostas para o Artefato: Modelo de Design para que sua evolução possa continuar.
A atualização dos diagramas de seqüência envolve os seguintes passos:
Observe que as interações de um objeto ativo são normalmente descritas usando colaborações de especificação e máquinas de estado. Elas seriam usadas aqui para mostrar como as mensagens podem ser enviadas para objetos ativos por outros elementos do sistema em uma realização maior de casos de uso. No uso típico, os objetos ativos são encapsulados dentro de subsistemas para a finalidade desta atividade, de modo que a realização de casos de uso consista em um conjunto de subsistemas em interação. As interações definem as responsabilidades e interfaces dos subsistemas. Dentro dos subsistemas, os objetos ativos representam threads simultâneos de execução. Os subsistemas permitem que o trabalho seja dividido entre as equipes de desenvolvimento, com as interfaces servindo como contratos formais entre as equipes.
Para os sistemas de tempo real, você usará o Artefato: Cápsulas para representar os objetos ativos.
Uma observação secundária sobre a exibição de mensagens originadas dos subsistemas (ou de suas classes de proxy): restringir as mensagens apenas a interfaces reduz o acoplamento entre os elementos do modelo e melhora a resiliência do design. Sempre que possível, tente fazer isso e, quando houver mensagens dos subsistemas para elementos do modelo que não sejam da interface, procure oportunidades para alterá-las para mensagens para interfaces a fim de melhorar o desacoplamento no modelo.

Documentação do comportamento de caso de uso executado pelos objetos em um diagrama de seqüência.
Quando tiver distribuído o comportamento entre os objetos, considere como o fluxo será controlado. Você encontrou os objetos presumindo que eles interagiriam de uma certa maneira na realização de casos de uso e teriam um determinado papel. Ao distribuir o comportamento, você pode começar a testar essas suposições. Em algumas partes do fluxo, você talvez deseje usar uma estrutura descentralizada; em outras, você pode preferir uma estrutura centralizada. Para obter definições sobre essas variantes e recomendações sobre quando usar os dois tipos de estrutura, consulte Diretrizes: Diagramas de Seqüência.
Você poderá precisar de novos objetos neste ponto, por exemplo, se estiver usando uma estrutura centralizada e precisar de um novo objeto para controlar o fluxo. Lembre-se de que qualquer objeto adicionado ao modelo de design deve atender aos requisitos feitos em relação do modelo de objeto.
Descreva cada variante de fluxo em um diagrama de seqüência separado. Diagramas de seqüência são geralmente preferíveis ao diagrama de colaboração já que sua leitura tende a ser mais fácil quando precisam conter o nível de detalhamento normalmente desejável durante o projeto do sistema.
Comece descrevendo o fluxo básico, que é o mais comum ou o fluxo de eventos mais importante. Em seguida, descreva as variantes, como os fluxos excepcionais. Você não precisará descrever todos os fluxos de eventos, desde que empregue e exemplifique todas as operações dos objetos participantes. Desse modo, fluxos muito triviais poderão ser omitidos, como aqueles que só se concentram em um objeto.
Estude o caso de uso para verificar se há outras variantes de fluxo além das já descritas na captura e na análise de requisitos, por exemplo, aquelas que dependem de implementação. À medida que for identificando novos fluxos, descreva cada um deles em um diagrama de seqüência. Veja a seguir alguns exemplos de fluxos excepcionais.
Você pode descrever um caminho alternativo de um fluxo como um fluxo opcional em vez de como uma variante. A lista a seguir inclui dois exemplos de fluxos opcionais.
Se desejar que um fluxo opcional ou qualquer subfluxo complexo fique especialmente destacado, use um diagrama de seqüência separado. Cada diagrama de seqüência separado deve ser referido no diagrama de seqüência do fluxo principal de eventos através de scripts, texto ou anotações nas margens para indicar onde o comportamento do subfluxo ou o comportamento opcional ocorre.
Quando o comportamento de fluxo opcional ou excepcional puder ocorrer em qualquer parte, por exemplo, o comportamento executado quando um determinado evento ocorre, o diagrama de seqüência do fluxo principal de eventos deverá ser anotado para indicar que quando o evento ocorrer, o comportamento descrito no diagrama de seqüência opcional/excepcional será executado. Você também pode, no caso de um comportamento significativo controlado por eventos, utilizar diagramas de estados para descrever o comportamento do sistema. Para obter mais informações, consulte Diretrizes: Diagrama de Estados.
Quando um caso de uso é realizado, o fluxo de eventos é geralmente descrito em termos dos objetos de execução, ou seja, como uma interação entre os objetos de design. Para simplificar os diagramas para identificar o comportamento reutilizável, talvez seja necessário encapsular um subfluxo de eventos dentro de um subsistema. Nesse caso, grandes subseções do diagrama de seqüência serão substituídas por uma única mensagem para o subsistema. Um diagrama de seqüência separado pode ilustrar as interações internas dentro do subsistema que fornecem o comportamento necessário (para obter mais informações, consulte Atividade: Design do Subsistema).
As subseqüências de mensagens dentro dos diagramas de seqüência deverão ser encapsuladas dentro de uma subsistema quando:

Uma realização de casos de uso pode ser descrita, se necessário, em vários níveis na hierarquia do subsistema. As linhas de vida no diagrama do meio representam subsistemas; as interações nos círculos representam a interação interna dos membros do subsistema em resposta à mensagem.
Vantagens dessa abordagem:
Exemplo:
Considere o diagrama de seqüência a seguir, que faz parte de uma realização do caso de uso Chamada Local:

Nesse diagrama, as classes exibidas em cinza pertencem ao subsistema Administração de Rede; as outras classes pertencem ao subsistema Administração de Assinantes. Isso implica em um diagrama de seqüência de vários subsistemas, ou seja, em um diagrama no qual todos os objetos que participam do fluxo de eventos foram incluídos, independentemente de suas classes estarem ou não em diferentes subsistemas.
Como alternativa, podemos mostrar a chamada de comportamento no subsistema Administração de Rede e o exercício de uma interface específica nesse subsistema. Vamos considerar que o subsistema Administração de Rede forneça uma interface ICoordinator, usada pelo subsistema Administração de Assinantes:

Essa interface é realizada pela classe Coordenador dentro de Administração de Rede. Nesse caso, podemos usar o próprio subsistema Administração de Rede e sua interface ICoordinator no diagrama de seqüência, em vez das instâncias de classes dentro de Administração de Rede:

Observe que as instâncias de classe Coordenador, Informações sobre Dígitos e Rede são substituídas pelo subsistema que as contêm. Todas as chamadas para o subsistema são feitas através da interface ICoordinator.
Para haver uma capacidade de substituição dos subsistemas que realizam a mesma interface, apenas as interfaces desses subsistemas devem estar visíveis nas interações (e nos diagramas em geral); caso contrário, as interações (ou os diagramas) precisarão ser alteradas quando um subsistema for substituído pelo outro.
Exemplo:
Podemos incluir apenas a interface ICoordinator, mas não seu subsistema provedor, em um diagrama de seqüência:

Enviar uma mensagem para uma linha de vida de interface significa que qualquer subsistema que realize a interface poderá ser substituído pela interface do diagrama. Observe que não há mensagens saindo da linha de vida da interface ICoordinator já que subsistemas diferentes que realizam a interface podem enviar diferentes mensagens. No entanto, se você desejar descrever os tipos de mensagens a serem enviadas (ou com permissão para serem enviadas) de qualquer subsistema que esteja realizando a interface, essas mensagens poderão partir da linha de vida da interface.
Em alguns casos, pode ser apropriado desenvolver um subsistema de forma mais ou menos independente e em paralelo com o desenvolvimento de outros subsistemas. Para isso, é necessário primeiro encontrar dependências de subsistema, identificando as interfaces entre eles.
O trabalho pode ser realizado da seguinte maneira:
Você também pode escolher se deseja ordenar os diagramas de seqüência em termos dos subsistemas ou em termos apenas de suas interfaces. Em alguns projetos, pode até ser necessário implementar as classes, fornecendo as interfaces antes de você continuar com o resto da modelagem.
Toda a meta do paradigma orientado a objetos é encapsular os detalhes de implementação. Portanto, com relação à persistência, gostaríamos de ter um objeto persistente que se parecesse exatamente como um objeto transiente. Não precisamos estar cientes de que o objeto é persistente ou tratá-lo de forma diferente do que trataríamos qualquer outro objeto. Pelo menos, essa é a meta.
Na prática, poderá haver ocasiões em que o aplicativo precisará controlar vários aspectos da persistência:
Existem dois casos a serem considerados: o tempo inicial no qual o objeto é gravado no armazenamento de objetos persistentes e as horas subseqüentes quando o aplicativo deseja atualizar o armazenamento de objetos persistentes com uma mudança realizada no objeto.
Em qualquer um desses dois casos, o mecanismo específico depende das operações suportadas pelo framework de persistência. Em geral, o mecanismo usado consiste no envio de uma mensagem para o framework de persistência para criar o objeto persistente. Uma vez que um objeto seja persistente, o framework de persistência é inteligente o suficiente para detectar mudanças subseqüentes no objeto persistente e gravá-las no armazenamento de objetos persistentes quando necessário (em geral, quando uma transação for comprometida).
Um exemplo de objeto persistente sendo criado é mostrado abaixo:

O objeto PersistenceMgr é uma instância de VBOS, um framework de persistência. O OrderCoordinator cria um Pedido persistente, enviando-o como argumento de uma mensagem 'createPersistentObject' para o PersistenceMgr.
Em geral, não é necessário modelar isso explicitamente a não ser que seja importante saber que o objeto está sendo armazenado explicitamente em um ponto específico, em alguma seqüência de eventos. Se as operações subseqüentes precisarem consultar o objeto, ele deverá existir no banco de dados e, portanto, será importante saber que o objeto existirá lá.
A recuperação de objetos do armazenamento de objetos persistentes é necessária antes que o aplicativo envie mensagens para esse objeto. Lembre-se de que o trabalho em um sistema orientado a objetos é executado através do envio de mensagens para objetos. Porém, se o objeto para o qual você deseja enviar uma mensagem estiver no banco de dados, mas ainda não estiver na memória, haverá um problema: você não poderá enviar uma mensagem para algo que ainda não existe!
Em resumo, você precisa enviar uma mensagem para um objeto que saiba como consultar o banco de dados, recuperar o objeto correto e instanciá-lo. Somente depois disso você poderá enviar a mensagem original que pretendia enviar. Às vezes, o objeto que cria instâncias de um objeto persistente é denominado objeto fábrica. Um objeto fábrica é responsável por criar instâncias de objetos, incluindo objetos persistentes. Com base em uma consulta, o objeto fábrica poderia ser projetado para retornar um conjunto de um ou mais objetos que correspondessem à consulta.
Em geral, os objetos estão muito conectados entre si através de suas associações, portanto, normalmente só é necessário recuperar o objeto raiz em um gráfico de objeto; o restante é basicamente 'extraído' de modo transparente do banco de dados através de suas associações com o objeto raiz. (Um bom mecanismo de persistência é inteligente nesse caso: ele apenas recupera objetos quando eles são necessários; caso contrário, acabaríamos criando instâncias de um grande número de objetos sem necessidade. A recuperação de objetos antes que sejam necessários é um dos principais problemas de desempenho causados por mecanismos de persistência simplistas.)
O exemplo a seguir mostra como a recuperação de um objeto do armazenamento de objetos persistentes pode ser modelada. Em um diagrama de seqüência real, o DBMS não seria mostrado já que deveria ser encapsulado no objeto fábrica.

O problema com objetos persistentes é que eles persistem! Ao contrário dos objetos transientes que simplesmente desaparecem quando o processo que os criou é eliminado, os objetos persistentes existem até serem excluídos explicitamente. Portanto, é importante que o objeto seja excluído quando ele não for mais usado.
O problema é que isso é difícil de ser determinado. Só porque um aplicativo terminou de usar o objeto não significa que todos os aplicativos, atuais e futuros, também terminaram de usá-lo. E como os objetos podem e devem ter associações que eles inclusive desconhecem, nem sempre é fácil decidir se é hora de excluir um objeto.
No design, isso pode ser representado semanticamente através de diagramas de estados: quando o objeto atinge o estado final, pode-se dizer que ele é liberado. Os desenvolvedores responsáveis pela implementação das classes persistentes poderão usar, então, as informações do diagrama de estados para disparar o comportamento apropriado do mecanismo de persistência a fim de liberar o objeto. A responsabilidade do Designer da realização de casos de uso é disparar as operações apropriadas para fazer com que o objeto atinja seu estado final no momento apropriado para exclusão do objeto.
Se um objeto estiver muito conectado com outros objetos, poderá ser difícil determinar se o objeto pode ser excluído. Como um objeto fábrica conhece a estrutura do objeto, assim como dos objetos aos quais está conectado, muitas vezes é útil carregar o objeto fábrica para uma classe responsável por determinar a exclusão ou não de uma instância específica. O framework de persistência também pode fornecer suporte para essa capacidade.
As transações definem um conjunto de chamadas de operação indivisíveis; elas são todas executadas ou nenhuma delas é executada. No contexto da persistência, uma transação define um conjunto de mudanças para um conjunto de objetos que são todas executadas ou nenhuma é executada. As transações oferecem consistência, assegurando que conjuntos de objetos passem de um estado consistente para outro.
Há várias opções que mostram transações em Realizações de Casos de Uso:

Representação das fronteiras de transação usando anotações textuais.

Diagrama de seqüência mostrando mensagens explícitas para iniciar e encerrar transações.
Se todas as operações especificadas em uma transação não puderem ser executadas (em geral, devido a um erro), a transação será anulada e todas as mudanças feitas durante a transação serão revertidas. Condições de erro antecipadas muitas vezes representam fluxos de eventos excepcionais em casos de uso. Em outros casos, as condições de erro ocorrem devido a alguma falha no sistema. As condições de erro também devem ser documentadas em interações. Exceções e erros simples podem ser mostrados na interação onde ocorreram; exceções e erros complexos podem exigir suas próprias interações.
Os modos de falha de objetos específicos podem ser mostrados em diagramas de estados. O fluxo condicional do tratamento de controle desses modos de falha pode ser mostrado na interação em que o erro ou a exceção ocorreu.
A simultaneidade descreve o controle de acesso a recursos críticos do sistema durante uma transação. Para manter o sistema em um estado consistente, uma transação pode exigir que ele tenha acesso exclusivo a determinados recursos-chave do sistema. A exclusividade pode incluir a capacidade de leitura de um conjunto de objetos, a gravação de um conjunto de objetos ou a leitura e a gravação de um conjunto de objetos.
Vamos examinar um exemplo simples verificando por que talvez seja necessário restringir o acesso a um conjunto de objetos. Imagine que estejamos executando um sistema simples de entrada de pedidos. As pessoas ligam para fazer pedidos e nós processamos e enviamos os pedidos. Podemos considerar o pedido como um tipo de transação.
Para ilustrar a necessidade do controle de simultaneidade, imagine que eu ligue para pedir um novo par de botas para caminhadas. Quando a entrada do pedido é feita no sistema, ele verifica se as botas que desejo, no tamanho correto, existem no estoque. Se existirem, fazemos a reserva desse par para que ninguém mais possa comprá-lo até que o pedido seja enviado. Quando o pedido é enviado, as botas são retiradas do estoque.
Durante o período entre o pedido e o seu envio, as botas ficam em um estado especial - elas existem no estoque, mas estão "comprometidas" com o meu pedido. Se o meu pedido for cancelado por algum motivo (se eu mudar de idéia ou o meu cartão de crédito tiver expirado), as botas voltarão para o estoque. Quando o pedido for enviado, vamos considerar que nossa pequena empresa não deseje manter um registro dessas botas.
A meta da simultaneidade, como nas transações, é assegurar que o sistema passe de um estado consistente para outro. Além disso, a simultaneidade procura assegurar que uma transação tenha todos os recursos de que precisa para concluir seu trabalho. O controle da simultaneidade pode ser implementado de várias maneiras diferentes, incluindo bloqueio de recursos, semáforos, travas de memória compartilhadas e espaços de trabalho privados.
Em um sistema orientado a objetos, é difícil saber apenas pelos padrões de mensagem se uma mensagem específica pode causar uma mudança de estado em um objeto. Além disso, diferentes implementações podem prevenir para a necessidade de restringir o acesso a determinados tipos de recursos; por exemplo, algumas implementações fornecem a cada transação sua própria visão do estado do sistema no início da transação. Nesse caso, outros processos poderão alterar o estado de um objeto sem afetar a 'visão' de qualquer outra transação em execução.
Para evitar restrição da implementação, no design desejamos simplesmente indicar os recursos aos quais a transação deve ter acesso exclusivo. Usando o exemplo anterior, desejamos indicar que precisamos de acesso exclusivo às botas que foram pedidas. Uma alternativa simples é anotar a descrição da mensagem que está sendo enviada, indicando que o aplicativo precisa de acesso exclusivo ao objeto. O Implementador poderá, então, usar essa informação para determinar a melhor maneira de implementar o requisito de simultaneidade. Um exemplo de diagrama de seqüência mostrando a anotação das mensagens que exigem acesso exclusivo é mostrado abaixo. A suposição é que todos os bloqueios sejam liberados quando a transação for concluída.

Exemplo mostrando o controle de acesso anotado em um diagrama de seqüência.
O motivo para a não restrição do acesso a todos os objetos necessários em uma transação é que muitas vezes apenas alguns objetos devem ter restrições de acesso; restringir o acesso a todos os objetos que participam de uma transação desperdiça recursos valiosos e poderia criar, em vez de evitar, gargalos de desempenho.
No fluxo de eventos da realização de casos de uso, você talvez precise acrescentar uma descrição aos diagramas de seqüência, nos casos em que o fluxo de eventos não fica totalmente claro apenas com o exame das mensagens enviadas entre os objetos participantes. Alguns exemplos incluem casos onde são necessárias anotações de tempo, observações sobre comportamento condicional ou algum esclarecimento sobre o comportamento da operação para facilitar a leitura dos diagramas por observadores externos.
O fluxo de eventos é descrito inicialmente na Atividade: Análise de Caso de Uso. Nesse passo, você refina o fluxo de eventos conforme necessário para esclarecer os diagramas de seqüência.
Muitas vezes, o nome da operação não é suficiente para entender por que a operação está sendo executada. Anotações de texto ou scripts na margem do diagrama podem ser necessários para esclarecer o diagrama de seqüência. Eles também podem ser necessários para representar o fluxo de controle, como passos de decisão, looping e ramificação. Além disso, marcas de texto talvez sejam necessárias para correlacionar os pontos de extensão no caso de uso com locais específicos nos diagramas de seqüência.
Exemplos anteriores dentro dessa atividade já ilustraram várias maneiras diferentes de anotar diagramas de seqüência.
À medida que os casos de uso vão sendo realizados, você precisará unificar as classes e os subsistemas identificados para assegurar a homogeneidade e a consistência no modelo.
Pontos a serem considerados:
Verifique o modelo de design neste estágio para se certificar de que seu trabalho esteja na direção certa. Não é necessário revisar o modelo em detalhes, mas considere os Pontos de Verificação para o Modelo de Design enquanto estiver trabalhando nele.
Consulte especialmente os pontos de verificação para a realização de casos de uso na Atividade: Revisar o Design.
|
Rational Unified Process
|