Modelação com UML
Casos de uso, classes, multiplicidades, sequência, atividades e estados sem confundir domínio com implementação.
A reserva R de Ana ocupa a sala A das 14h às 15h. Queremos representar a quem pertence, que objetivo realiza e como o ecrã obtém confirmação. Cada diagrama escolhe uma dessas perguntas e omite pormenores que não ajudam a responder-lhe.
UML é uma linguagem de modelação, não um processo de desenvolvimento. Os modelos precisam de ser consistentes entre si e com as regras do sistema.
Casos de uso
Um ator é um papel externo que interage com o sistema. Pode ser uma pessoa ou outro sistema. Uma pessoa pode desempenhar vários papéis. Um caso de uso descreve comportamento do sistema que produz um resultado observável com valor para um ator.
A fronteira indica o sistema que estamos a modelar. Se modelarmos só a aplicação móvel, o servidor pode ser um ator externo. Se modelarmos o sistema completo, o servidor faz parte da solução.
Na aplicação de reservas, o estudante reserva uma sala. Essa operação inclui verificar disponibilidade. Pedir equipamento só acontece se o estudante o solicitar, no ponto de extensão equipamento de Reservar sala. Equipamento pertence a uma versão futura fora do âmbito inicial, por isso aparece aqui como extensão possível, não como compromisso da primeira entrega.
A associação entre ator e caso de uso é uma linha. A relação include é uma dependência tracejada que parte do caso que inclui e aponta para o caso incluído. Mostra um comportamento incorporado no caso base, que fica incompleto sem ele.
A relação extend também é tracejada, mas aponta no sentido contrário: parte da extensão para o caso base. Liga-se num ponto de extensão e aplica-se quando a condição indicada se verifica; se não houver condição, aplica-se sempre que o ponto é atingido. O caso base tem significado sem a extensão.
Por isso não uses extend para ligar duas ações só porque uma pode acontecer depois da outra: recuperar acesso pode ser um objetivo independente e não uma extensão de iniciar sessão. A relação precisa de representar incorporação de comportamento, não proximidade no menu.
Descrever um caso de uso
Uma elipse não especifica os passos e exceções. Para “Reservar sala”, escrevemos:
| Campo | Descrição |
|---|---|
| Ator principal | Estudante |
| Pré-condição | O estudante está autenticado |
| Gatilho | O estudante pede a reserva |
| Fluxo principal | Escolhe sala e intervalo; o sistema verifica regras e disponibilidade; confirma e apresenta a reserva |
| Alternativa | Se o intervalo deixou de estar livre, o sistema informa o conflito e não cria a reserva |
| Pós-condição de sucesso | Existe uma reserva confirmada em nome do estudante |
| Garantia em caso de falha | Não fica uma reserva parcialmente criada |
Uma pré-condição diz o que assumimos antes do caso. Não substitui um passo que precisa de tratar uma situação possível. Se a autenticação puder expirar durante a operação, define o comportamento dessa falha.
Classes e modelo de domínio
Um modelo de domínio representa conceitos e relações do problema, como Estudante, Reserva e Sala. Não pretende enumerar todos os ecrãs, bases de dados e classes técnicas.
Uma classe descreve objetos com características comuns. Os compartimentos podem apresentar nome, atributos e operações. Num modelo de domínio podemos omitir operações técnicas que ainda não decidimos.
A multiplicidade numa ponta diz quantas instâncias dessa classe podem relacionar-se com uma instância da outra classe. Partindo de Reserva, lemos 1 junto de Estudante: cada reserva pertence a exatamente um estudante. Partindo de Estudante, lemos 0..* junto de Reserva: um estudante pode ter nenhuma ou várias reservas.
A multiplicidade não proíbe sobreposições de horários, por isso precisamos de uma restrição adicional: duas reservas confirmadas da mesma sala não se sobrepõem. Estrutura e regras complementam-se.
Relações entre classes
Uma associação exprime uma relação estrutural. Uma dependência, normalmente tracejada, indica que um elemento usa outro e pode ser afetado pela sua alteração.
A generalização liga uma especialização ao conceito geral, com triângulo vazio apontado para o geral. Só faz sentido quando a especialização pode ser tratada como esse conceito. “Administrador usa a aplicação” não demonstra que precisamos de uma subclasse Administrador.
A composição tem losango cheio na ponta do todo. Representa propriedade forte das partes; uma parte pertence no máximo a um todo composto de cada vez, e a destruição do todo implica a destruição das partes que ainda lhe pertencem. Não escolhas composição apenas porque uma classe contém uma referência. Um exemplo positivo seria Reserva e LinhaDeReserva se cada linha só existisse dentro de uma reserva e fosse apagada com ela. Sala não é esse caso.
A agregação partilhada, de losango vazio, tem uma semântica mais fraca; uma associação simples costuma ser mais clara quando não precisamos de expressar uma relação todo-parte precisa. Na aplicação, Sala não é parte composta de Reserva, pois a sala continua a existir depois de apagar a reserva; também não há razão para pôr uma base de dados no modelo de domínio apenas por guardar os objetos.
Sequência e colaboração
Um diagrama de sequência mostra participantes e mensagens num cenário. A linha de vida prolonga-se para baixo e o tempo avança nessa direção. O nome de um participante pode identificar a instância e o tipo, como ecrã:Ecrã. :Ecrã identifica apenas o tipo de um participante sem lhe dar um nome.
Neste desenho detalhado, a regra de concorrência pertence ao serviço e ao repositório do servidor. A operação criarSeLivre verifica e cria atomicamente, em vez de fazer uma verificação local seguida de uma gravação desprotegida.
Uma chamada síncrona espera pela resposta; uma mensagem assíncrona permite ao emissor continuar. Uma resposta pode ser omitida quando fica clara. O fragmento alt separa alternativas com guardas, opt descreve um comportamento opcional e loop uma repetição. As guardas têm de explicar as condições.
As mensagens devem corresponder a responsabilidades possíveis dos participantes. Se o diagrama manda o ecrã confirmar uma reserva sem consultar a autoridade dos dados, o desenho revela um erro, mesmo que as setas estejam bem desenhadas.
Atividades e estados
Um fluxo com fork e join
Um diagrama de atividades mostra um fluxo de ações. Uma decisão escolhe uma saída com base numa guarda; uma junção reúne caminhos alternativos. Uma bifurcação, fork, inicia fluxos concorrentes; uma sincronização, join, espera pelos fluxos que deve reunir.
Na figura, o losango separa confirmação e conflito. As barras iniciam atualização da lista e envio da notificação em paralelo e reúnem esses fluxos antes de apresentar o resultado. Se a atualização falhar e a notificação seguir, precisamos de definir o tratamento da falha parcial, não apenas desenhar o join.
Transições de uma reserva
A reserva de Ana começa às 14h. Às 13h59 pode passar de Confirmada para Cancelada; às 14h o mesmo pedido é recusado. Quando chega às 15h, passa para Terminada. O efeito de cancelamento pertence à primeira transição, enquanto uma ação de saída de Confirmada correria também na passagem para Terminada.
Uma máquina de estados descreve como um objeto reage a eventos. Na Reserva, podemos ter Confirmada, Cancelada e Terminada.
Um cancelamento só passa de Confirmada para Cancelada se o início ainda não tiver ocorrido. A transição pode escrever-se evento [guarda] / efeito. Por exemplo, cancelar [agora < início] / libertar intervalo liga Confirmada a Cancelada: a guarda [agora < início] é a condição necessária e o efeito acontece quando a transição é tomada. Uma ação de saída de Confirmada ocorreria em todas as saídas desse estado, por isso não é o mesmo que o efeito específico de cancelar.
| Estado atual | Evento | Condição | Estado seguinte e efeito |
|---|---|---|---|
| Confirmada | cancelar | Agora é anterior ao início e o pedido vem do titular | Cancelada; o intervalo deixa de estar reservado |
| Confirmada | cancelar | Agora é igual ou posterior ao início | Mantém Confirmada; o sistema recusa |
| Confirmada | atingir o fim | A hora de fim chegou | Terminada |
| Cancelada | cancelar novamente | Qualquer hora | Mantém Cancelada; não cria nem liberta outra reserva |
A tabela fixa as regras deste exemplo e não presume que pedidos repetidos devolvam sucesso; esse resultado precisa de um contrato próprio. Estados descrevem situações do objeto, não nomes de ecrãs: “Página inicial” não é um estado de Reserva. Um diagrama de estados pode ajudar a encontrar testes para transições válidas e recusadas.
Conferir modelos
Segue um cenário com dados concretos. Uma reserva R pertence ao estudante Ana e ocupa a sala A das 14h às 15h. Confirma que o modelo de domínio admite essa estrutura, que o caso de uso descreve a criação e que a sequência respeita a regra de disponibilidade.
Depois muda uma condição: a sala está ocupada ou a rede falha. O modelo deve explicar o resultado sem inventar outra regra a meio. Na arquitetura, escolhemos os módulos que concretizam estas responsabilidades.
Exercícios
Incluir uma verificação e estender uma reserva
Reservar sala incorpora sempre Verificar disponibilidade. No ponto de extensão equipamento, o estudante pode pedir equipamento. Cancelar reserva é outro objetivo, realizado mais tarde. Desenha as duas dependências, indica a condição da extensão e explica se Cancelar deve estender Reservar só por acontecer depois.
Pista
Solução
A seta tracejada com «include» parte de Reservar sala e aponta para Verificar disponibilidade. A seta tracejada com «extend» parte de Pedir equipamento e aponta para Reservar sala, no ponto equipamento, com condição “o estudante pediu equipamento”. Reservar continua a produzir uma reserva sem essa extensão. Cancelar é um objetivo independente: ocorrer depois não significa inserir comportamento na execução de Reservar. Não desenhamos essa relação extend. A associação ator-caso é uma linha contínua diferente.
Confere a tua resposta:
- Desenhei include de Reservar sala para Verificar disponibilidade.
- Desenhei extend de Pedir equipamento para Reservar sala, com ponto de extensão e condição.
- Justifiquei que reservar continua a ter significado sem pedir equipamento.
- Não usei extend para representar a ordem temporal de cancelar.
Erros frequentes
Não uses uma linha contínua só porque a inclusão faz parte do caso. Não escolhas extend para qualquer passo opcional de interface.
Quem pode ter várias reservas?
Na associação Estudante e Reserva, há 1 junto de Estudante e 0..* junto de Reserva. Explica as duas pontas e diz se isso impede horários sobrepostos.
Pista
Solução
Cada reserva pertence a um estudante; cada estudante pode ter zero ou várias reservas, ou seja o modelo admite várias reservas e não impede sobreposição. É necessário definir a restrição temporal da sala e, se existir, a do estudante.
Confere a tua resposta:
- Disse que cada reserva tem exatamente um estudante.
- Disse que um estudante pode ter zero ou mais reservas.
- Expliquei que a sobreposição exige outra restrição.
Erros frequentes
Não leias 0..* como cada reserva ter vários estudantes. Não atribuas uma regra temporal a uma multiplicidade.
Sala é parte da reserva?
Uma Reserva referencia uma Sala. Uma colega propõe composição com losango cheio junto de Reserva. Avalia.
Pista
Ter uma referência não basta para justificar composição.
Solução
A composição não representa a relação descrita. A sala existe independentemente e pode relacionar-se com outras reservas. Uma associação Reserva-Sala, com 1 junto de Sala e 0..* junto de Reserva, exprime a relação.
Confere a tua resposta:
- Usei propriedade e ciclo de vida para avaliar composição.
- Observei que a sala existe sem a reserva.
- Propus uma associação com multiplicidades adequadas.
Erros frequentes
Não uses losango por estética nem para qualquer campo que guarda um objeto.
Uma guarda e um efeito
Uma reserva começa às 14h e está Confirmada. Escreve a transição que permite ao titular cancelá-la às 13h59 e diz o que acontece às 14h. Libertar o intervalo deve ser efeito dessa transição ou uma ação de saída genérica de Confirmada, se também houver uma passagem para Terminada?
Pista
Mais uma pista
Solução
De Confirmada para Cancelada usamos
cancelar [pedido do titular e agora < início] / libertar intervalo.
Às 13h59 a guarda é verdadeira e a transição liberta o intervalo.
Às 14h a guarda é falsa e o sistema mantém Confirmada. O efeito
pertence ao cancelamento. Uma ação de saída de Confirmada correria
também na passagem para Terminada. Se a intenção é executar este efeito
apenas quando se cancela, a ação de saída é demasiado abrangente. O fim
normal já deixa de ocupar instantes futuros pela regra do intervalo.
Confere a tua resposta:
- Liguei Confirmada a Cancelada pelo evento cancelar, com guarda agora < início e pedido do titular.
- Coloquei libertar intervalo como efeito dessa transição.
- Mantive Confirmada e recusei o pedido no instante de início.
- Expliquei que uma ação de saída correria também numa saída para Terminada.
Erros frequentes
A alternativa que deixou o fluxo parado
Neste modelo simplificado, cada ação termina sem falhas. Uma decisão escolhe pagar por cartão ou pagar por referência. Ambos os caminhos entram numa barra join que exige chegada dos dois. Depois devem começar, em paralelo, emitir recibo e atualizar o histórico. Uma segunda barra espera pelas duas ações antes de apresentar o resultado. Explica por que a primeira barra pode bloquear o fluxo e redesenha as junções necessárias.
Pista
Solução
A decisão escolhe apenas um meio de pagamento. O join espera por duas chegadas, mas só uma será produzida: nesse modelo, o fluxo fica parado. Reunimos cartão e referência numa junção de alternativas, representada por um losango, que deixa seguir o caminho que chegou. Depois um fork inicia emitir recibo e atualizar histórico. Agora o join final pode esperar pelas duas ações, porque ambas foram iniciadas. Decisão e junção combinam alternativas; fork e join separam e sincronizam fluxos concorrentes.
Confere a tua resposta:
- Diagnostiquei que só um dos caminhos alternativos chega ao join.
- Usei uma junção de alternativas antes do fork.
- Usei o join apenas para esperar por recibo e atualização iniciados em paralelo.