Atividade:
|
Finalidade | |
Função: Arquiteto de Software | |
Freqüência: Uma vez por iteração | |
Etapas | |
Artefatos de Entrada: | Artefatos Resultantes: |
Mentores de Ferramentas: | |
Informações Adicionais:
|
|
Detalhes de Workflow: |
A Atividade: Análise de Caso de Uso resulta em classes de análise, que representam itens conceituais que podem desempenhar comportamento. Em design, classes de análise evoluem para vários tipos de elementos de design:
Além disso, no design, é necessário identificar:
Essas distinções mais refinadas permitem examinar diferentes aspectos do design:
Separando assuntos e lidando com cada questão representada por esses conceitos isoladamente, é possível simplificar o processo de design e esclarecer as soluções.
Se a rastreabilidade precisar ser mantida entre modelos de subsistemas, isso deverá ser documentado durante esta atividade. Para obter informações adicionais sobre como documentar a rastreabilidade entre o Modelo de Design e outros modelos do sistema, consulte Diretrizes: Modelo de Design.
Finalidade | Identificar os sinais e eventos externos e internos aos quais o sistema deve responder. |
Eventos são ocorrências externas e internas que provocam uma ação no sistema. Os eventos e suas características podem ajudar a conduzir a identificação dos principais elementos de design, como classes ativas.
Uma lista inicial de eventos externos pode originar-se do Modelo de Caso de Uso, das interações dos atores com os casos de uso. Eventos internos podem originar-se do texto nos fluxos de casos de uso ou podem ser identificados conforme a evolução do design.
São características importantes dos eventos:
As características dos eventos devem ser capturadas conforme necessário para conduzir a identificação dos elementos de design que os tratam. A captura das características dos eventos costuma ser mais importante em sistemas reativos (dirigidos a eventos), mas pode ser útil em outros sistemas, como naqueles com simultaneidade e/ou mensagens assíncronas.
Eventos de comunicação assíncrona podem ser modelados como Sinais para
expressar os dados que transportam ou as relações entre os sinais, como
generalização. Em alguns sistemas, particularmente em sistemas reativos, é importante
relacionar os sinais recebidos de dispositivos externos com mecanismos específicos, como
interrupções ou mensagens de polling específicas.
Finalidade | Refinar as classes de análise em elementos de modelo de design apropriados |
Identificar Classes. Quando a classe de análise for simples e já representar uma abstração lógica individual, poderá ser mapeada diretamente, 1:1, para uma classe de design. Em geral, as classes de entidade permanecem relativamente intactas no Design. Como também costumam ser persistentes, decida se a classe de design deverá ser persistente e registre a sua decisão na descrição correspondente.
Ao identificar classes, agrupe-as em Artefato: Pacotes de Design, para fins de gerenciamento organizacional e de configuração. Consulte Diretrizes: Pacote de Design para obter informações adicionais sobre como tomar decisões sobre empacotamento.
Identificar Classes Ativas. Considere os requisitos de simultaneidade do sistema no contexto dos objetos de análise identificados, isto é, há necessidade de que o sistema responda aos eventos gerados externamente e, se for o caso, quais classes de análise estarão 'ativas' quando os eventos ocorrerem? Os eventos externos no Modelo de Casos de Uso são representados por estímulos provenientes de atores que interagem com um caso de uso. Observe as Realizações de Casos de Uso correspondentes para ver os objetos que interagem quando um evento ocorre. Comece agrupando os objetos em conjuntos autônomos de objetos de colaboração - esses agrupamentos representam um recorte inicial em um grupo que pode formar uma classe ativa composta.
Se os eventos tiverem atributos importantes que precisam ser capturados, considere a possibilidade de modelá-los como classes, <<sinal>> estereotipado.
As instâncias de classes ativas representam threads de execução 'lógicos' independentes. Esses threads de execução 'lógicos' não devem ser confundidos com os threads de execução do sistema operacional nem ser mapeados literalmente para eles (embora, em algum momento, eles sejam mapeados para os threads de execução do sistema operacional). Ao contrário, eles representam threads de execução conceituais independentes no espaço de solução. A nossa meta ao identificá-los nesta etapa do design é permitir a divisão da solução em unidades independentes com base nas 'junções de simultaneidade' naturais do sistema. A divisão do trabalho dessa forma simplifica, do ponto de vista conceitual, os problemas que envolvem a simultaneidade, já que é possível lidar com threads de execução independentes separadamente, exceto no ponto em que eles compartilham classes passivas subjacentes.
Em geral, convém considerar uma classe ativa sempre que houver simultaneidade e conflitos de simultaneidade na área do problema. Use uma classe ativa para representar um objeto simultâneo externo ou uma atividade simultânea no computador. Com isso, é possível monitorar e controlar atividades simultâneas.
Outra opção natural é utilizar classes ativas como representativas internas de dispositivos físicos externos que estão conectadas a um computador, já que essas entidades físicas são inerentemente simultâneas. As classes de "driver de dispositivo" servem não só para monitorar e controlar os dispositivos físicos correspondentes, mas também para isolar o restante do sistema das especificações dos dispositivos. Isso significa que o restante do sistema poderá não ser afetado mesmo se a tecnologia dos dispositivos subjacente evoluir.
As classes ativas também são usadas para representar atividades simultâneas lógicas. Uma atividade lógica representa um "objeto" simultâneo conceitual, por exemplo, uma transação financeira ou uma ligação telefônica. Embora elas não sejam manifestadas diretamente como entidades físicas (apesar de ocorrerem no mundo físico), há geralmente motivos para tratá-las como tal. Por exemplo, talvez seja necessário reter temporariamente uma transação financeira específica para evitar um conflito de simultaneidade ou talvez seja necessário anulá-la devido a falhas no sistema. Como esses objetos conceituais precisam ser manipulados como uma unidade, convém representá-los como objetos com interfaces próprias que oferecem os recursos funcionais apropriados.
Um controlador de objetos ativos é um exemplo específico desse tipo de objeto conceitual. A sua finalidade é gerenciar de forma contínua outros objetos ativos. Normalmente, isso envolve colocar cada objeto no estado operacional desejado, mantê-lo nesse estado apesar das diversas interrupções, como falhas parciais, e sincronizar a operação correspondente com a operação de outros objetos. Os controladores de objetos ativos costumam evoluir dos objetos de Controle identificados durante a Atividade: Análise de Caso de Uso.
Devido à sua capacidade de resolver conflitos de simultaneidade de forma simples e correta, as classes ativas também são úteis como guardiãs de recursos compartilhados. Nesse caso, um ou mais recursos exigidos por diversas atividades simultâneas são encapsulados na classe ativa. Devido à sua semântica de exclusão mútua interna, as guardiãs protegem automaticamente os recursos contra conflitos de simultaneidade.
Identificar Subsistemas. Quando a classe de análise for complexa, de forma que pareça incluir comportamentos que não podem ser de responsabilidade de uma única classe que atue sozinha, deverá ser mapeada para um subsistema de design. Esse subsistema é usado para encapsular colaborações de tal modo que os clientes dele podem não ter conhecimento nenhum do seu design interno, mesmo quando usam os serviços que ele oferece.
Um subsistema é modelado como um componente da UML, que só possui interfaces como elementos públicos. As interfaces fornecem uma camada de encapsulamento, permitindo que o design interno do subsistema permaneça oculto dos outros elementos do modelo. O subsistema de conceitos é utilizado para distingui-lo dos pacotes, que são contêineres sem semântica de elementos de modelo.
A decisão de criar um subsistema a partir de um conjunto de classes de análise colaboradoras se baseia, em grande parte, no fato de que a colaboração poderá ser ou será desenvolvida de forma independente por uma equipe de design distinta. Se for possível incluir todas as colaborações em um pacote com as classes colaboradoras, um subsistema poderá permitir uma forma de encapsulamento mais sólida do que a permitida por um pacote simples. O conteúdo e as colaborações de um subsistema estão isolados completamente, atrás de uma ou mais interfaces, de forma que o cliente dele dependa apenas da interface. Dessa forma, o designer do subsistema está completamente isolado das dependências externas; o designer (ou a equipe de design) precisa especificar como a interface é realizada, mas tem permissão total para alterar o design interno do subsistema sem afetar as dependências externas. Em sistemas grandes, com equipes bastante independentes, esse grau de desacoplamento, combinado com a imposição arquitetural fornecida pelas interfaces formais, são um argumento sólido para escolher subsistemas em vez de pacotes simples. Consulte Diretrizes: Subsistema de Design para obter informações adicionais sobre os fatores que afetam a opção por utilizar subsistemas como elementos de design.
Finalidade | Identificar os elementos de design que formalizam as junções no sistema. |
As interfaces definem um conjunto de operações que são realizadas por um classificador. No Modelo de Design, as interfaces são usadas principalmente para definir as interfaces de subsistemas. Isso não quer dizer que também não podem ser usadas para classes, mas, para uma classe única, em geral basta definir operações públicas na classe que, de fato, determinam sua 'interface'. As interfaces são importantes para subsistemas porque permitem a separação da declaração do comportamento (a interface) da realização dele (as classes específicas no subsistema que realizam a interface). Esse desacoplamento permite aumentar a independência das equipes de desenvolvimento que trabalham em partes distintas do sistema e, ao mesmo tempo, reter definições precisas dos 'contratos' entre essas diferentes partes.
Para cada subsistema, identifique um conjunto de sugestões de interfaces.
Usando as colaborações agrupadas identificadas no passo anterior, identifique a
responsabilidade que é 'ativada' quando a colaboração é iniciada. Para refinar depois
essa responsabilidade, determine quais informações devem ser fornecidas pelo 'cliente' e
quais são retornadas quando a colaboração é concluída; esses conjuntos de informações
passam a ser os parâmetros de entrada e saída do protótipo e retornam um valor para uma
operação que o subsistema realizará. Defina um nome para essa operação, utilizando as
convenções de nomenclatura definidas no
Artefato: Diretrizes Específicas do Projeto.
Repita esse procedimento até definir todas as operações que serão realizadas pelo
subsistema.
Em seguida, agrupe as operações de acordo com as responsabilidades relacionadas. Convém criar grupos pequenos em vez de grupos grandes, já que é mais provável obter um conjunto coeso de responsabilidades comuns se há poucas operações no grupo. Fique atento também à reutilização - procure semelhanças que possam facilitar a identificação da funcionalidade reutilizável relacionada. No entanto, não gaste muito tempo tentando encontrar o agrupamento de responsabilidades ideal; lembre-se de que esse é apenas um agrupamento inicial, que continuará a ser refinado de forma iterativa durante a fase de elaboração.
Procure por similaridades entre as interfaces. No conjunto de sugestões de interfaces, procure nomes, responsabilidades e operações semelhantes. Reajuste as interfaces que apresentam as mesmas operações, extraindo as operações comuns para uma nova interface. Não se esqueça de verificar também as interfaces existentes, reutilizando-as sempre que possível. A meta é manter a coesão das interfaces e, ao mesmo tempo, remover operações redundantes entre elas. Isso facilitará a compreensão e o desenvolvimento das interfaces ao longo do tempo.
Defina dependências entre interfaces. Os parâmetros e o valor de retorno de cada operação da interface são de um tipo específico: eles realizam uma determinada interface ou devem ser instâncias de um tipo de dados simples. Nos casos em que os parâmetros são objetos que realizam uma interface específica, defina relacionamentos de dependência entre a interface e as interfaces das quais ela depende. A definição de dependências entre interfaces fornece informações de acoplamento úteis ao arquiteto de software, já que essas dependências definem as principais dependências entre elementos no modelo de design.
Mapeie as interfaces para subsistemas. Uma vez identificadas as interfaces, crie associações de realização entre o subsistema e as interfaces realizadas por ele. Uma realização do subsistema para uma interface indica que existem um ou mais elementos no subsistema que realizam as operações da interface. Posteriormente, quando o subsistema for projetado, as realizações entre ele e a interface serão refinadas, e o designer do subsistema definirá os elementos específicos dele que realizam as operações da interface. Essas realizações refinadas podem ser visualizadas apenas pelo designer do subsistema; para o cliente do subsistema, apenas a realização entre o subsistema e a interface está visível.
Defina o comportamento especificado pelas interfaces. As interfaces em geral definem uma máquina de estado implícita para os elementos que realizam a interface. Se for necessário disparar as operações da interface em uma ordem específica (por exemplo, é necessário ativar a conexão com o banco de dados antes de usá-lo), será preciso definir uma máquina de estados com os estados visíveis (ou inferidos) publicamente que deverão ser suportados pelos elementos de design que realizam a interface. Essa máquina de estados ajudará o usuário da interface a compreendê-la melhor e ajudará o designer dos elementos que realizam a interface a especificar o comportamento adequado deles.
Empacote as interfaces. As interfaces são de propriedade do arquiteto de software; mudanças nas interfaces são sempre arquiteturalmente significativas. Para gerenciar essas mudanças, é necessário agrupar as interfaces em um ou mais pacotes pertencentes ao arquiteto de software. Se cada interface for realizada por um único subsistema, será possível colocá-las no mesmo pacote com o subsistema. Se forem realizadas por mais de um subsistema, deverão ser colocadas em um outro pacote pertencente ao arquiteto de software. Com isso, é possível gerenciar e controlar as interfaces independentemente dos próprios subsistemas.
De acordo com a UML 1.5, um subsistema é, efetivamente, um tipo especial de pacote que só possui interfaces como elementos públicos. As interfaces fornecem uma camada de encapsulamento, permitindo que o design interno do subsistema permaneça oculto dos outros elementos do modelo. O subsistema de conceito é utilizado para distingui-lo dos pacotes "comuns", que são contêineres sem semântica de elementos de modelo; o subsistema representa um uso específico de pacotes com propriedades (comportamentais) semelhantes a classe.
Consulte Diferenças entre UML 1.x e UML 2.0 para obter informações adicionais.
Rational Unified Process
|