Atividade:
| |||||||||||||||||||||
| Finalidade | |
| Passos | |
Artefatos Informados:
|
Artefatos Resultantes: |
| Freqüência: Uma vez por iteração | |
| Papel: Arquiteto de Software | |
| Detalhamentos do Fluxo de Trabalho: |
A Atividade: Análise de Caso de Uso resulta em classes de análise que representam itens conceituais que podem apresentar um comportamento. No design, as classes de análise evoluem para diversos tipos de elementos de design:
Além disso, no design, é necessário identificar:
Essas distinções mais refinadas permitem examinar diferentes aspectos do design:
Nos sistemas em tempo real, as cápsulas são usadas no lugar das classes ativas e oferecem uma semântica mais sólida para simplificar o design e aumentar a confiabilidade dos aplicativos simultâneos. As cápsulas compartilham alguns aspectos das classes e dos subsistemas: na verdade, são colaborações encapsuladas de classes que juntas representam um thread de controle no sistema. A diferença entre elas e os subsistemas está no fato de que uma cápsula é a responsabilidade de um único designer, enquanto um subsistema é a responsabilidade (em geral) de uma equipe de desenvolvedores; no entanto, um subsistema pode conter cápsulas.
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.
Finalidade
|
Os eventos são ocorrências externas e internas que causam alguma ação no sistema; eles podem ocorrer de forma aleatória e independente uns dos outros. A necessidade de responder a eventos de forma oportuna é um fator essencial para os requisitos de simultaneidade no sistema. A especificação dos eventos aos quais o sistema deverá responder e a caracterização deles orientarão a identificação das partes essenciais da arquitetura do sistema.
Para identificar eventos aos quais o sistema deverá responder, observe os casos de uso e as realizações associadas. Para cada estímulo que um ator envia para o sistema, considere como o sistema toma conhecimento do estímulo: o sistema está monitorando o usuário, aguardando uma resposta ou o recebimento é disparado por um dispositivo ou sensor que detecta o evento?
As interrupções do sistema são um mecanismo comum pelo qual ele toma conhecimento, de forma assíncrona, de que algo interessante ocorreu. São mais úteis quando usadas para transmitir informações sobre eventos ocasionais (fatos que não ocorrem em intervalos regulares) que exigem resposta imediata. Poderão gerar um evento de chamada ou evento de sinal, ou, se tiverem sido provenientes de um relógio ou timer, um evento de tempo.
A varredura é mais apropriada nos casos em que os eventos ocorrem em um ciclo natural e periódico ou ocorrem de modo aleatório, mas de forma mais ou menos contínua, e nos casos em que o sistema não precisa responder de imediato. Nesses tipos de eventos, o sistema poderá armazenar os eventos em um buffer e responder a eles em intervalos periódicos; o uso de interrupções traria gastos e desperdício de tempo. Também é possível usar a varredura para detectar a satisfação de uma condição, o que levará à ocorrência de um evento de mudança.
Um aspecto importante da arquitetura é determinar quais eventos deverão ser respondidos de imediato e como o sistema informará a chegada deles, ou seja, para caracterizá-los como um evento de chamada (em que o chamador aguarda a resolução do evento), um evento de mudança (que significa uma mudança de estado em uma condição Boolean), um evento de sinal (que significa o recebimento de uma entidade de sinal para comunicação assíncrona, em que o emissor não perde o controle quando o sinal é enviado) ou um evento de tempo (que significa que um tempo absoluto foi atingido ou determinado período já se esgotou). A escolha do tipo de evento influenciará consideravelmente a forma da arquitetura.
Para os eventos que não exigem resposta imediata, o arquiteto de software deverá determinar quais poderão ser respondidos periodicamente e em que intervalos o sistema deverá atender os eventos (eventos diferentes poderão exigir intervalos de atendimento distintos). É possível representar isso por meio de propriedades no próprio evento que indiquem a prioridade ou a latência tolerada, bem como a freqüência média esperada e a distribuição da freqüência do evento.
Um sinal é uma entidade nomeada explícita, destinada à comunicação assíncrona explícita entre objetos - em geral (mas não exclusivamente), o objeto receptor e possivelmente o objeto emissor serão modelados como objetos ativos. Um sinal possui uma lista explícita de parâmetros cujos valores são especificados pelo emissor no momento em que a instância do sinal é enviada. Um evento de sinal é o recebimento por um objeto de um sinal enviado para ele, e uma instância de evento de sinal é associada apenas a uma instância de sinal.
Use os sinais para permitir a comunicação entre as partes internas do sistema, bem como para informar falhas ou exceções, embora você possa usá-los em qualquer circunstância que exija a comunicação assíncrona. Em geral, os sinais também são implementados por meio de interrupções, embora também seja possível usar técnicas de varredura. A importância de identificar sinais na arquitetura é estabelecer regras comuns de como e quando eles serão usados, bem como identificar os mecanismos de sinalização a serem usados pelos sinais (p. ex., interrupções específicas ou mensagens de varredura). Se isso não for considerado, o resultado poderá ser desastroso e levar a um comportamento do sistema bastante imprevisível.
Observe que um evento de sinal é manipulado por uma máquina de estados e o recebimento de um sinal poderá ou não disparar uma transição na máquina (dependendo do estado no momento em que o sinal é recebido), causando a execução de uma ação.
Nos sistemas em tempo real, as cápsulas estão limitadas a usar sinais para a comunicação entre elas, e é possível estabelecer a comunicação síncrona com o uso de pares de sinais, um em cada direção.
Finalidade
|
| Mais Informações: |
| Mentores de Ferramentas: |
Identifique Classes. Quando a classe de análise é simples e já representa uma única abstração lógica, pode 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 no Artefato: Pacote de Design, para fins de gerenciamento da organização e de configuração. Consulte Diretrizes: Pacote de Design para obter mais informações sobre como tomar decisões sobre empacotamento.
Identifique Classes Ativas. Considere os requisitos de simultaneidade do sistema no contexto dos objetos de análise identificados: o sistema precisa responder aos eventos gerados externamente e, se precisar, quais classes de análise estão 'ativas' quando os eventos ocorrem? 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 colaboradores - esses agrupamentos representam um recorte inicial em um grupo que pode formar uma classe ativa composta.
Nos sistemas em tempo real, é necessário agrupar os conjuntos de objetos identificados em cápsulas, que possuem uma semântica sólida de encapsulamento.
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 é usar classes ativas como representantes internas de dispositivos físicos externos que estão conectados 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 deles. 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, como, por exemplo, uma transação financeira ou 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 exemplo específico desse tipo de objeto conceitual é um controlador de objetos ativos. 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 se desenvolver a partir de 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.
Nos sistemas em tempo real, as cápsulas deverão ser usadas no lugar das classes ativas: onde quer que você tenha identificado a necessidade de uma classe ativa de acordo com a heurística descrita anteriormente, ela deverá ser substituída por uma cápsula.
Identifique 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. Do ponto de vista lógico, um subsistema de design é equivalente ao Artefato: Componente no Artefato: Modelo de Implementação.
Na realidade, um subsistema é um tipo especial de pacote que possui apenas 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 é usado para distingui-lo dos pacotes "comuns", que são contêineres sem semântica de elementos do modelo; o subsistema representa um uso particular dos pacotes com propriedades (comportamentais) do tipo classe.
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 mais informações sobre os fatores que afetam a escolha de subsistemas como elementos de design.
Finalidade
|
| Mentor de Ferramentas: Gerenciamento de Interfaces Usando o Rational Rose |
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 o nome da operação, usando as convenções de nomeação definidas no Artefato: Guia de Design. 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 semelhanças entre 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 retornado de cada operação de interface são de um tipo específico: devem realizar 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. Após a identificação das interfaces, crie associações de realização entre o subsistema e as interfaces que ele realiza. 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. Em geral, as interfaces definem uma máquina de estados implícita para os elementos que as realizam. 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 pertencem ao arquiteto de software; as mudanças nelas efetuadas são sempre significativas do ponto de vista da arquitetura. Para gerenciar essas mudanças, é necessário agrupar as interfaces em um ou mais pacotes pertencentes ao arquiteto de software. Se todas as interfaces forem realizadas por um único subsistema, será possível colocá-las na fachada do 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.
|
Finalidade
|
Os protocolos são semelhantes às interfaces em sistemas orientados por eventos: eles identificam o 'contrato' entre cápsulas definindo um conjunto combinado de sinais, usados para a comunicação entre threads de controle independentes. Enquanto as interfaces são usadas principalmente para definir um serviço de mensagens síncrono através de um modelo de chamada de funções, os protocolos são usados principalmente para definir a comunicação assíncrona através de um serviço de mensagens baseado em sinais. Os protocolos permitem a separação da declaração do comportamento (o conjunto de sinais) da realização dele (os elementos 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 cápsula, identifique um conjunto de sinais de entrada e saída. Usando as colaborações agrupadas identificadas nos passos anteriores, 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 do protótipo para um sinal que a cápsula realizará através de uma de suas portas. Defina o nome do sinal, usando as convenções de nomeação definidas no Artefato: Guia de Design. Repita esse procedimento até definir todos os sinais que serão realizados pelo subsistema.
Em seguida, agrupe os sinais 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á poucos sinais 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. Atribua ao protocolo um nome significativo que descreva o papel que ele desempenha nas colaborações de cápsula.
Procure semelhanças entre protocolos. No conjunto de sugestões de protocolos, procure nomes, responsabilidades e sinais semelhantes. Reajuste os protocolos que apresentam os mesmos sinais, extraindo os sinais comuns para uma nova interface. Não se esqueça de verificar também os protocolos existentes, reutilizando-os sempre que possível. A meta é manter a coesão dos protocolos e, ao mesmo tempo, remover sinais redundantes entre eles. Isso facilitará a compreensão e o desenvolvimento dos protocolos ao longo do tempo.
Mapeie os protocolos para cápsulas. Após a identificação dos protocolos, crie portas nas cápsulas que os realizam. As portas da cápsula definem suas 'interfaces', o comportamento que pode ser solicitado dela. Posteriormente, quando ela for projetada, o comportamento especificado pelas portas será descrito pela máquina de estados da cápsula.
Defina o comportamento especificado pelos protocolos. Em geral, os protocolos definem uma máquina de estados implícita para os elementos que realizam a interface. Se for necessário receber os sinais de entrada na interface em uma ordem específica (p. ex., é necessário receber um sinal 'o sistema está pronto' antes de receber um sinal de erro específico), 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 o protocolo. Essa máquina de estados ajudará o usuário das cápsulas que realizam o protocolo a entender melhor o comportamento delas e ajudará o designer das cápsulas a apresentar o comportamento adequado para os elementos correspondentes.
Empacote os protocolos. Os protocolos pertencem ao arquiteto de software; as mudanças neles efetuadas são sempre significativas do ponto de vista da arquitetura. Para gerenciar essas mudanças, é necessário agrupá-los em um ou mais pacotes pertencentes ao arquiteto de software. Com isso, é possível gerenciar e controlar os protocolos independentemente das cápsulas que os realizam.
|
Rational Unified Process
|