Diagramas UML

Ler e relacionar classes, sequências, comunicação e estados sem confundir estrutura com execução.

TLDR

Perguntar sobre esta página

ChatGPTClaudePerplexityGrokGeminiCopiar e abrirDeepSeekCopiar e abrir

A arena conhece o herói, mas essa ligação não diz em que ordem um pedido move o herói e atualiza a vista. UML tem diagramas para perguntas diferentes: classes mostram estrutura; sequências e comunicação mostram mensagens; estados mostram reações permitidas.

Diagrama de classes

Uma classe pode ter três compartimentos: nome, atributos e operações. + significa público, - privado, # protegido e ~ visível no pacote. Atributos e operações estáticos aparecem sublinhados; elementos abstratos podem aparecer em itálico ou com indicação de abstrato.

Uma operação pode mostrar parâmetros e retorno, como + mover(x: int): boolean. Uma interface pode usar o estereótipo «interface». Os nomes são tipos; num diagrama de objetos, heroi:Heroi identificaria uma instância.

Começa por um exemplo concreto. Uma Arena guarda um Heroi e Paredes; o Heroi conhece a sua Posicao. A arena compõe paredes e herói porque o seu ciclo de vida manda neles; a ligação a Posicao é uma associação navegável simples, sem afirmar posse exclusiva. Cada relação da tabela seguinte afirma uma coisa diferente sobre estas ligações.

RelaçãoNotaçãoO que afirma
AssociaçãoLinha contínuaObjetos mantêm uma relação estrutural. Uma seta aberta pode indicar navegação.
DependênciaSeta tracejadaUm elemento usa outro, por exemplo como parâmetro ou variável temporária.
GeneralizaçãoLinha e triângulo vazio para a baseUm tipo especializa outro e deve respeitar o contrato da base.
RealizaçãoLinha tracejada e triângulo vazio para a interfaceUma classe implementa o contrato.
Agregação partilhadaLosango vazio do lado do todoRelação todo/parte fraca; a semântica de posse precisa de ser definida no modelo.
ComposiçãoLosango cheio do lado do todoPosse forte: uma parte pertence no máximo a um todo composto de cada vez.

Na composição, o modelo associa o ciclo de vida da parte ao todo: eliminar o todo elimina as partes que ainda lhe pertencem. Uma parte pode ser retirada ou transferida antes disso. Não interpretes esta regra conceptual como uma chamada automática ao garbage collector de Java.

Uma referência num campo Java não prova composição. Usa associação quando não estás a afirmar posse forte. Também não assumes navegação nas duas direções apenas porque a linha não mostra uma seta; o desenho pode deixar essa informação por especificar.

Traduzir relações para Java

Uma classe Equipa pode especializar Grupo e realizar a interface Participante. Em Java, a base vem depois de extends e as interfaces depois de implements:

class Equipa extends Grupo implements Participante {
    private final java.util.Set<Membro> membros =
        new java.util.HashSet<Membro>();
}

O excerto pressupõe que Grupo, Participante e Membro existem e que os métodos exigidos já são herdados ou implementados. Uma classe só estende uma classe, mas pode implementar várias interfaces, separadas por vírgulas. Escrever implements Participante extends Grupo troca a ordem da gramática e não compila.

O campo membros pode representar uma associação navegável para zero ou mais membros, sem duplicados lógicos. O conjunto precisa da igualdade adequada de Membro. Uma multiplicidade mínima de um também precisaria de validação na construção e nas remoções; Java não a impõe por declarar um campo. O campo e o final na referência também não provam composição UML ou imutabilidade do conjunto.

Multiplicidades

A multiplicidade num extremo diz quantos objetos desse extremo podem relacionar-se com um objeto do outro extremo. 1 é exatamente um; 0..1, opcional; 0..* ou *, zero ou mais; 1..*, pelo menos um.

Uma Arena contém um Heroi e zero ou mais Paredes. Heroi tem uma associação navegável para uma Posicao.Arena- largura: int+ mover(dx): boolean1Heroi- vida: int1Posicao- x, y: int0..*Parede

Neste modelo escolhemos que uma arena possui um herói e zero ou mais paredes. O losango fica na arena, o todo. O herói conhece uma posição. A associação à posição não afirma que a posição só possa ser usada por esse herói.

Se cada herói só pertence a uma arena, a multiplicidade no extremo da arena também seria 1. O desenho reduzido mostra apenas as multiplicidades necessárias à pergunta sobre o conteúdo da arena; num modelo completo, explicita os dois extremos e os nomes dos papéis.

Uma associação pode ter dados próprios. Por exemplo, a relação entre aluno e projeto pode guardar papel e data de entrada. Uma classe de associação representa esses dados da relação, em vez de os duplicar em cada aluno ou projeto.

Diagrama de sequência

Os participantes têm linhas de vida. O tempo avança de cima para baixo e representa ordem, não duração proporcional à distância vertical. Uma mensagem de chamada tem como destino o objeto que oferece a operação.

Uma seta de chamada síncrona usa uma ponta preenchida; uma mensagem assíncrona usa uma ponta aberta. Um retorno, quando o mostramos, é tracejado. Uma ativação é um retângulo fino sobre a linha de vida que indica execução. O esquema abaixo mostra chamadas síncronas e omite as ativações para destacar o percurso.

Modelo começa na posição 2. Controlador pede mover(1), recebe true, lê a posição 3 e pede à Vista para mostrar 3. O tempo avança para baixo.ControladorModeloVistamover(1)truegetX()3mostrar(3)

O modelo começa na posição 2; o controlador pede mover(1), onde o argumento é um deslocamento. O modelo aceita, passa para a posição 3 e devolve true; depois o controlador lê a posição e pede à vista mostrar(3), que mostra o resultado aceite pelo modelo, não apenas a posição pedida pelo utilizador.

Uma mensagem para o próprio objeto representa uma chamada interna. Uma criação inicia a linha de vida do novo participante; uma destruição termina-a com uma cruz.

Condições e repetição

Um fragmento alt separa percursos alternativos por guardas, como [livre] e [ocupada]. opt representa um percurso opcional. loop contém mensagens repetidas enquanto uma condição ou contagem o determina.

O modelo começa em 2 e a tentativa pretende chegar a 3. Aqui acrescentamos uma regra de ocupação: só entra na célula se estiver livre. Esta regra não existe no modelo reduzido de MVC, que verifica apenas os limites. O fragmento alt representa os dois resultados da tentativa:

A leitura getX() e a chamada mostrar(x) ficam depois do fragmento porque ocorrem nos dois casos. No ramo livre, x vale 3; no ocupado, vale 2. Escrever mostrar(3) nos dois percursos esconderia a recusa. Uma chamada a Mapa.livre() dentro de mover seria numerada 1.1 no diagrama de comunicação.

Uma guarda é uma condição entre parênteses retos. Não é um método novo do objeto nem uma garantia de que os outros ramos são impossíveis. As guardas devem descrever as condições reais do cenário.

Diagrama de comunicação

Representa os participantes e as ligações por onde enviam mensagens. Como não há eixo vertical de tempo, a numeração fixa a ordem.

Controlador liga-se ao Modelo e à Vista. As mensagens são 1 mover(1), 2 getX() e 3 mostrar(3), pela ordem indicada.c:Controladorm:Modelov:Vista1: mover(1)2: getX()3: mostrar(3)

Aqui 1, 2 e 3 são chamadas consecutivas do controlador. Se mover chamasse uma operação interna noutro participante, essa chamada poderia ser 1.1; uma segunda chamada dentro da mesma ativação seria 1.2. A numeração hierárquica mostra o aninhamento, não apenas uma lista global.

Uma repetição pode ser indicada por *[condição]. Sequência e comunicação podem descrever a mesma interação, mas a sequência torna a ordem mais visível e a comunicação destaca as ligações.

Diagrama de estados

Um estado representa uma situação em que o objeto responde a eventos de determinada maneira. A mesma ação pausar só faz sentido durante o jogo, não no menu.

Jogo começa em Menu. Comecar passa a Jogar. Pausar passa a Pausa, retomar regressa a Jogar e perder termina o jogo.MenucomeçarJogarpausarPausaretomarperder

O círculo preenchido indica o início; o círculo com outro dentro indica o fim. O jogo começa em Menu, começar leva a Jogar, pausar leva a Pausa e retomar regressa a Jogar. perder termina este percurso.

Uma transição pode ter a forma evento [guarda] / efeito. Em mover [célula livre] / atualizar posição, o evento desencadeia a tentativa, a guarda decide se é permitida e o efeito descreve o que acontece.

entry é uma ação ao entrar num estado e exit ao sair. Uma transição externa para o mesmo estado volta a executar saída e entrada; uma reação interna pode tratar o evento sem sair do estado. Esta diferença importa se a entrada reinicia um temporizador.

Estados compostos agrupam uma máquina interna. Por exemplo, Jogar pode conter Normal e Invencível. Não confundas esse agrupamento com uma subclasse Java: o diagrama descreve comportamento permitido, não exige uma implementação específica.

Regiões ortogonais

Um estado composto pode conter regiões ortogonais, cada uma com o seu estado ativo. Podemos separar o estado da partida, Ativo ou Pausa, do áudio, Audível ou Mudo. Se as duas dimensões forem independentes, as quatro configurações são permitidas:

PartidaÁudio
AtivoAudível
AtivoMudo
PausaAudível
PausaMudo

Representar duas regiões evita criar quatro nomes de estado que repitam as mesmas regras. Com n dimensões binárias independentes, um modelo plano pode precisar de 2n2^n combinações. As regiões reduzem a repetição do desenho; não fazem desaparecer as combinações nem implicam criar uma thread Java por região.

Também podem existir várias transições com o mesmo evento e a mesma origem, distinguidas por guardas. Em Menu, começar [dados válidos] pode levar a Jogar e começar [dados inválidos] pode apresentar um erro e manter Menu. Define condições que se excluem quando precisas de uma escolha determinada; não assumes que duas transições concorrentes serão escolhidas pela ordem em que as desenhaste.

Conferir o desenho com o contrato

Percorre uma situação concreta, como mover de 2 para 3 com a célula livre. Confirma que cada operação chamada existe no recetor, que há uma ligação para o conhecer e que o estado permite a ação. Repete com a célula bloqueada.

Se o código guarda apenas Modelo, o desenho não deve mostrar uma dependência direta do controlador para todas as suas peças internas. Se o desenho e o código diferem, decide qual está correto para o contrato e corrige o outro. Um diagrama serve antes da implementação para discutir escolhas e depois para explicar o que ficou.

Exercícios

Ler o extremo certo

Uma associação liga Equipa e Pessoa. No extremo Pessoa está 1..3, e no extremo Equipa está 0..1. Explica o que significa cada multiplicidade.

Pista
Cada extremo conta objetos daquele tipo.
Mais uma pista
Para uma equipa, olha para o número junto de Pessoa.
Solução

Lê cada extremo para um objeto do outro lado: fixa uma equipa e conta pessoas em 1..3; fixa uma pessoa e conta equipas em 0..1. Cada equipa tem de uma a três pessoas. Cada pessoa pode não ter equipa ou pertencer a uma. A multiplicidade não significa que existem apenas três pessoas no sistema inteiro.

Confere a tua resposta:

  • Uma equipa relaciona-se com uma a três pessoas.
  • Uma pessoa relaciona-se com zero ou uma equipa.
  • Lê cada multiplicidade para um objeto do extremo oposto.

Erros frequentes

Não troques o sentido de leitura nem interpretes a multiplicidade como total global.

Voltar à explicação
Numerar uma chamada aninhada

A mensagem 2 é Controlador para Modelo.validar. Durante essa chamada, Modelo chama Mapa.livre. Qual numeração mostra essa chamada dentro da ativação 2?

A tua resposta

Pista
O prefixo conserva o número da chamada exterior.
Mais uma pista
Acrescenta o nível da primeira chamada interna.
Solução

A mensagem é 2.1: livre(...). Outra chamada dentro de validar poderia ser 2.2. A numeração torna visível o aninhamento que uma lista plana perde.

  • 2.1. É a primeira chamada aninhada na ativação 2.
  • 3. 3 seria uma mensagem seguinte ao mesmo nível de 2.
  • 1.2. Esse número pertence a uma chamada dentro da ativação 1.

Erros frequentes

Uma chamada interna não termina a ativação exterior antes de acontecer.

Voltar à explicação
Desenhar o estado aceite nos dois ramos

O modelo começa na posição 2. mover(1) só chega a 3 se a célula estiver livre; caso contrário, devolve false e mantém a posição. O controlador pede a tentativa e atualiza sempre a vista. Desenha a sequência com um fragmento alt, os retornos e a atualização final. Explica que valor recebe mostrar em cada ramo.

Pista
A operação comum aos ramos tem o mesmo nome, mas o seu resultado pode variar.
Solução
Controlador -> Modelo: mover(1)
alt [célula 3 livre]
    Modelo -> Modelo: atualizar posição para 3
    Modelo --> Controlador: true
else [célula 3 ocupada]
    Modelo --> Controlador: false
end
Controlador -> Modelo: getX()
Modelo --> Controlador: x
Controlador -> Vista: mostrar(x)

No ramo livre, x = 3; no ocupado, x = 2. A chamada à vista fica fora do alt porque ocorre sempre. O seu argumento vem do estado do modelo depois da tentativa, não do destino calculado pelo controlador.

Confere a tua resposta:

  • Parti de 2 e mostrei 3 no ramo livre, 2 no ocupado.
  • Coloquei os retornos true e false nos ramos respetivos.
  • Li getX() e chamei mostrar(x) depois do fragmento alt.
Voltar à explicação
Seguir uma transição com guarda

O jogo está em Menu. A única transição de saída é começar [podeComecar] / iniciarPartida, com destino Jogar. Chega começar com a guarda falsa; depois chega outro começar com a guarda verdadeira. Indica o estado após cada evento, quantas vezes corre iniciarPartida e a função dos três elementos da transição. Não existem outras reações a estes eventos.

Pista

Receber o evento não basta para tomar uma transição cuja guarda falha.

Solução

O primeiro evento deixa o jogo em Menu e não executa iniciarPartida. No segundo, a guarda passa, o efeito corre e o destino é Jogar. O efeito executou-se uma vez. começar é o evento recebido, podeComecar é a condição que permite a transição e iniciarPartida é o efeito dessa transição. A recusa não é uma entrada em Jogar seguida de regresso a Menu.

Confere a tua resposta:

  • Mantive Menu quando podeComecar é falso.
  • Entrei em Jogar e executei iniciarPartida uma vez quando a guarda passou.
  • Distingui o evento, a guarda e o efeito sem executar este último na recusa.
Voltar à explicação
Traduzir estrutura sem inventar posse

O modelo diz que Equipa especializa Grupo, realiza Participante e conhece zero ou mais Membro, sem duplicados lógicos nem ordem exigida. Grupo tem construtor sem argumentos. Participante declara int quantidade(). Membro tem equals/hashCode compatíveis e imutáveis.

Escreve a declaração de Equipa, o campo de membros e quantidade(), começando com a coleção vazia. Uma proposta escreve class Equipa implements Participante extends Grupo: é Java válido? O campo final prova composição UML? Se a multiplicidade fosse 1..*, que regra adicional seria necessária?

Solução
class Equipa extends Grupo implements Participante {
    private final java.util.Set<Membro> membros =
        new java.util.HashSet<Membro>();
    @Override public int quantidade() { return membros.size(); }
}

A base precede as interfaces na gramática Java. A proposta que troca essa ordem não compila. Set satisfaz unicidade lógica sem exigir ordem; final impede reatribuir a referência, mas não bloqueia mudanças no conjunto nem expressa posse forte.

Para 1..*, não poderíamos começar com zero membros: o construtor teria de garantir pelo menos um, e as operações de remoção não poderiam eliminar o último enquanto o objeto continuasse válido. Essa regra pertence ao contrato, não nasce automaticamente da declaração do campo.

Confere a tua resposta:

  • Escrevi extends Grupo antes de implements Participante.
  • Representei membros por uma coleção sem duplicados lógicos e implementei quantidade().
  • Distingui campo final e associação de composição UML e da validação de uma multiplicidade mínima.
Voltar à explicação

À tua maneira

Aparência

Modo de cor
Tema
Cor de destaque

Leitura

Fonte de leitura

Álgebra, lógica e uma ideia de cada vez.

Código

Fonte do código

Navegação

Cursor de leitura, sem edição. h, j, k, l movem; / pesquisa nesta página. v seleciona caracteres; V seleciona linhas; y copia. Tab permite destacar ou comentar. Espaço ou Enter abre blocos recolhíveis. Nos cartões, h, j, k, l mudam o foco.

CSS personalizado

Ativa uma sugestão ou guarda o teu CSS. Só muda este navegador.

Usa --page, --text e --accent para acompanhar o tema.

Seletores e recuperação

.site-header, .prose, .course-card, [data-reading-history], [data-playground] e .page-actions.

Abre a aparência com a tecla vírgula ou no rodapé. Se o CSS esconder os controlos, abre esta página sem CSS personalizado.

Pesquisar

Escreve para pesquisar em todo o site.

para escolher · Enter para abrir · Esc para fechar

Copiar fórmula

A cópia automática não está disponível. Copia o LaTeX abaixo.

Pesquisar nesta página

Enter procura · n / N repetem · Esc cancela. Ctrl / Cmd K pesquisa todo o site.

Atalhos de teclado

Clica numa tecla para a mudar. Esc cancela. Backspace desativa.

PesquisarCtrl / Cmd K

Os atalhos não interferem enquanto escreves. Tab e Enter funcionam sempre.