Atividade:
|
Finalidade
|
|
Função: Designer | |
Freqüência: Uma vez por iteração, para um conjunto de Artefato: Projetar Realizações de Casos de Uso. | |
Etapas | |
Artefatos de Entrada: | Artefatos Resultantes: |
Mentores de Ferramentas: | |
More Information: | |
Detalhes de Workflow: |
O comportamento de um sistema pode ser descrito utilizando várias técnicas - 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.
O Artefato: Projetar Realização de Casos de Uso fornece uma maneira de rastrear o comportamento do Modelo de Design no Modelo de Casos de Uso e organiza as colaborações no Modelo de Design com base no conceito de Caso de Uso.
Crie uma Realização de Casos de Uso de Design no Modelo de Design para cada Caso de Uso a ser projetado. O nome da Realização de Casos de Uso de Design deve ser o mesmo do Caso de Uso associado e uma relação de "realizações" deve ser estabelecida, da realização de casos de uso para seu respectivo caso de uso associado.
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. As versões anteriores desses diagramas podem ter sido criadas durante a Atividade: Análise de Casos de Uso. Tais "versões de análise" das realizações de casos de uso descrevem interações entre classes de análise. Elas precisam ser desenvolvidas para descrever interações entre elementos de design.
A atualização dos diagramas de seqüência envolve os seguintes passos:
Observe que esses são os diagramas de seqüência no nível do sistema que mostram como interagem as instâncias dos elementos de design de nível superior (normalmente, subsistemas e interfaces de subsistemas). Diagramas de seqüência mostrando o design interno de subsistemas são produzidos separadamente, como parte da Atividade: Design de Subsistema.
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 utilizadas aqui para mostrar como as mensagens podem ser enviadas para ativar objetos por outros elementos do sistema em uma realização de caso de uso maior. 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.
Uma nota secundária sobre a exibição das mensagens provenientes dos subsistemas: a restrição de mensagens somente a interfaces reduz o acoplamento entre elementos de modelo e aprimora 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, pode ser preferível utilizar uma estrutura descentralizada; em outras, prefira uma estrutura centralizada. Para obter as definições dessas variantes e recomendações sobre quando utilizar os dois tipos de estrutura, consulte Diretrizes: Diagramas de Seqüência.
Você poderá precisar de novos objetos nesse ponto, por exemplo, se estiver utilizando 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.
Durante a Atividade: Análise Arquitetural, os mecanismos de
análise foram identificados. Durante a Atividade:
Identificar Mecanismos de Design, os mecanismos de análise são refinados em
mecanismos de design, o mapeamento dos mecanismos de análise para os mecanismos de design
é capturado no Documento de Arquitetura de
Software e os mecanismos de design são documentados nas
Diretrizes Específicas do Projeto.
Durante essa atividade, Design de Caso de Uso, todos os mecanismos de design
aplicáveis são incorporados nas realizações de casos de uso. O designer pesquisa
os mecanismos de design disponíveis e determina aqueles que se aplicam à realização de
casos de uso que está sendo desenvolvida, trabalhando dentro das recomendações e
diretrizes documentadas no Documento de Arquitetura de Software e nas Diretrizes
de Design.
Nota: O mecanismo de design aplicável pode ter sido identificado na
Atividade: Análise de Casos de Uso, durante a qual as classes
de análise podem ter sido "marcadas" com um mecanismo de análise específico,
indicando que uma determinada parte da funcionalidade precisou ser tratada no design.
Em tal caso, os mecanismos de design aplicáveis são aqueles associados aos mecanismos de
análise com os quais foram marcadas as classes de análise que participam da realização de
casos de uso
O Designer incorpora os mecanismos de design aplicáveis nas realizações de casos de uso, incluindo nas realizações de casos de uso os elementos de design necessários e as interações de elementos de design, conforme as regras de uso documentadas nas Diretrizes de Design.
Descreva cada variante de fluxo em um diagrama de seqüência separado. Diagramas de seqüência geralmente são preferíveis ao diagrama de comunicação, visto que sua leitura tende a ser mais fácil quando o diagrama precisa conter o nível de detalhe que normalmente se deseja obter ao projetar o 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 informações adicionais, 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. No sistema, um diagrama de seqüências separado pode ilustrar as interações internas do subsistema que fornecem o comportamento exigido (para obter informações adicionais, consulte Atividade: Design de 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 seguinte diagrama de seqüência, 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 a linha de vida da interface ICoordinator não possui mensagens de saída, visto que diferentes subsistemas que realizam a interface poderão enviar mensagens diferentes. 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. Por isso, com relação à persistência, é preferível ter um objeto persistente que se pareça exatamente 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. Essa pelo menos é a meta.
Na prática, talvez haja ocasiões em que o aplicativo precise 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 em que o aplicativo deseja atualizar o armazenamento de objetos persistentes com uma alteração 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 explicitamente isso, a menos que seja importante saber que o objeto está sendo armazenado explicitamente em um momento específico de uma 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. Mas se o objeto para o qual você deseja enviar uma mensagem estiver no banco de dados mas não na memória ainda, haverá um problema: você não conseguirá 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. O objeto que instancia um objeto persistente às vezes é chamado de depósito de informações. Um objeto depósito de informações é responsável por criar instâncias de objetos, incluindo objetos persistentes. Com base em uma consulta, o depósito de informações poderia ser designado para retornar um conjunto de um ou mais objetos que correspondessem à consulta.
Em geral, os objetos estão tão bem conectados uns aos outros através de suas associações que é comum ser necessário recuperar apenas o objeto raiz em um gráfico de objetos; o restante é "extraído" do banco de dados de forma essencialmente transparente, de acordo com suas respectivas associações com o objeto raiz. (Um mecanismo de persistência adequado é sensível a isso: ele só recupera objetos quando eles são necessários; caso contrário, acabaríamos tentando instanciar 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üências real, o DBMS não seria mostrado, visto que deveria ser encapsulado no objeto depósito de informações.
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. Por isso, é importante excluir o objeto quando ele não estiver mais sendo utilizado.
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 não só podem como de fato possuem associações sobre as quais nem mesmo sabem, nem sempre é fácil descobrir se é correto excluir um objeto.
Em design, isso pode ser representado semanticamente, utilizando gráficos de estado: quando o objeto atinge o estado final, pode-se dizer que ele está 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 é chamar as operações apropriadas para fazer com que o objeto atinja seu estado final quando é adequado excluí-lo.
Se um objeto estiver muito bem conectado a outros objetos, poderá ser difícil determinar se ele pode ser excluído. Visto que um objeto depósito de informações conhece a estrutura do objeto e também dos demais aos quais ele está conectado, muitas vezes é útil carregar o objeto depósito de informações com uma classe cuja responsabilidade seja determinar se uma instância específica pode ser excluída. A estrutura de persistência também pode fornecer suporte para esse recurso.
As transações definem um conjunto de chamadas de operação que são indivisíveis, isto é, ou 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 não for possível executar todas as operações de uma transação (geralmente, por causa de um erro ocorrido), a transação será interrompida 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 do motivo pelo qual poderemos precisar restringir o acesso a um conjunto de objetos. Digamos que esteja em execução 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 de controle de simultaneidade, vamos supor que eu ligue solicitando um novo par de botas para caminhada. Quando a entrada do pedido é feita no sistema, ele verifica se as botas que desejo, no tamanho correto, existem no estoque. Se existirem, é feita a reserva desse par para que ninguém mais possa comprá-las antes do envio do pedido. Quando o pedido é enviado, as botas são retiradas do estoque.
Durante o período entre o pedido ser feito e enviado, as botas ficam em um estado especial—elas estão no estoque, mas estão "comprometidas" com 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 podem alterar o estado de um objeto sem afetar a 'visualizaçã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 é delineado 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.
Conforme os casos de uso são realizados, é necessário unificar as classes e subsistemas de design identificados para assegurar a homogeneidade e consistência do Modelo de Design.
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 há necessidade de revisar o modelo de design detalhadamente mas, enquanto trabalha nele, leve em consideração os Pontos de verificação para o Modelo de Design.
Consulte especificamente pontos de verificação para realização de casos de uso na Atividade: Revisar o Design.
É possível utilizar uma classe proxy para representar o subsistema em diagramas de seqüência. Essa classe está contida no subsistema e é usada para representar o subsistema em diagramas que não suportam o uso direto de pacotes e subsistemas como elementos comportamentais. Utilize a classe proxy nos casos em que deseja mostrar que um subsistema específico responde a uma mensagem. Nesse caso, é possível mostrar as mensagens que estão sendo enviadas do proxy do subsistema para outros objetos.
Consulte Diferenças entre UML 1.x e UML
2.0 para obter informações adicionais.
Rational Unified Process
|