Ei devs! Quantas vezes foi lhes pedido o desenvolvimento de um diagrama da UML, e não conseguistes decidir qual ferramenta case usar, dado à pluralidade destas. Pois bem! Hoje lhe apresentaremos mais uma! Mas uma com uma abordagem um pouco diferente.
Nesta, ao invés de criar componentes de diagramas arrastando e soltando, nós iremos codificá-los! Isso mesmo! Vocês não ouviram errado! A proposta do PlantUML é criar diagramas da UML, como Caso de Uso, Classe, Sequência, entre outros usando uma linguagem própria, de uma forma simples e fácil!
Antes de iniciarmos você precisará ter instalado/configurado em seu computador a linguagem Java, um editor de texto e um interpretador de comandos. Com isto poderás iniciar!
Bom, vamos neste post, criar passo-a-passo um diagrama de classes, considerando uma classe já existente. No entanto, este processo, poderá ser aplicado para qualquer classe, ok?
Identifique os elementos do diagrama
Bom, iniciamos identificando os elementos que comporão nosso Diagrama de Classes. Para isso, vamos utilizar a classe-exemplo JButton, que possui documentação (no formato JavaDoc) bem definida e que nos permite obter os elementos em questão. Ao visitar o link, podemos elencar os seguintes elementos:
- Classe/Class
- Atributo/Field
- Método/Method
- Pacote/Package
- Relacionamentos: Interface
- Relacionamentos: Herança (Generalização/Especialização)
Codifique os elementos
Bom, com os elementos já identificados, vamos codificá-los.
Primeiro realizamos o download do arquivo plantuml.jar no link. Em seguida criamos um arquivo TXT no mesmo diretório do JAR baixado. Neste exemplo, nomeamos o arquivo como classDiagram.txt
Feito isso, abrimos o arquivo TXT em um editor de texto de nossa preferência, e neste redijimos:
@startuml classDiagram
@enduml
As palavras-reservadas @startuml e @enduml indicam que todo o código digitado entre elas, usará a linguagem/sintaxe plantuml. Já o classDiagram, indica o nome que será dado ao arquivo de saída. Deste modo, optamos por um nome sugestivo.
Agora criamos entre as palavras-reservadas a classe JButton, utilizando a sintaxe: class className <<stereotype>>.
@startuml classDiagram
class JButton <<Class>>
@enduml
Nota: O uso do stereotype é opcional, mas o utilizaremos neste post, a fim de distinguir Classes, Interfaces e Classes Abstratas, mesmo que o programa já gere uma letra (inicial maiúscula) identificadora.
Feito isso, testamos o resultado, abrindo nosso interpretador de comandos (terminal (Linux) | prompt de comando (Windows)), redigindo:
cd /path/to/txt-jar-file-directory
java -jar plantuml.jar classDiagram.txt
Em linhas gerais estas instruções fazem:
- Acessa o diretório onde se encontram os arquivos JAR e TXT. Atentem-se em substituir o trecho em negrito pelo caminho correspondente ao vosso computador. Se você for usuário Windows, este trecho deverá se parecer com X:\path\to\txt-jar-file-directory, onde X, representa o disco local.
- Gere um arquivo no formato de saída padrão (PNG), a partir do arquivo classDiagram.txt. A sintaxe do comando percebida é: java -jar plantumlFileName.jar sourceFileName.txt
Após a execução das instruções um arquivo PNG, nomeado classDiagram.png é esperado no mesmo diretório do arquivo TXT, tendo a seguinte aparência:
Figura 1 - Representação da classe JButton.
Fonte: Autor (CC BY-NC-SA 4.0)
Pois bem, agora temos que adicionar os atributos e/ou métodos à nossa classe. Esta classe-exemplo, em especial, não possui atributos, mas possui métodos (e construtores). Portanto, focaremos nestes.
Como dito anteriormente a classe JButton nos é apresentada no formato JavaDoc, um formato de documentação padrão para desenvolvedores Java... para felicidade de alguns, e ódio de outros, rs... Esta documentação nos apresenta os métodos utilizando a seguinte assinatura:
modifier returnType methodName(typeParam1 nameParam1, ...)
Exemplos:
public void setDefaultCapable(boolean defaultCapable)
• public = modifier (modificador)
• void = returnType (tipo de retorno/tipo do método)
• setDefaultCapable = methodName (nome do método)
• boolean = typeParam (tipo do parâmetro)
• defaultCapable = nameParam (nome do parâmetro)
public AccessibleContext getAccessibleContext()
• public = modifier (modificador)
• AccessibleContext = returnType (tipo de retorno/tipo do método)
• getAccessibleContext = methodName (nome do método)
Já a especificação UML nos indica a assinatura:
modifier methodName(nameParam1: typeParam1, ...): returnType
Cuja representação dos mesmos métodos se daria da seguinte forma:
+ setDefaultCapable(defaultCapable: boolean)
+ getAccessibleContext(): AccessibleContext
Considerando isto, faz-se necessário adequarmos todos os métodos (e/ou atributos) conforme proposto pela UML. Este pode ser um processo tedioso, e que nos exigirá muita atenção, mas que será muito importante em nosso crescimento em documentação de software. Assim façamos.
Nota 1: Modifier void, indica que este método não tem retorno. Para estes casos, o returnType é omitido.
Nota 2: Quanto aos modificadores atentemos aos símbolos da UML, que são: - (private), # (protected), ~ (package) e + (public).
Nota 3: Nossa classe-exemplo possui métodos e construtores (que também são métodos). Para diferenciá-los, basta verificar que métodos construtores obrigatoriamente são nomeados com o mesmo nome da classe. Isto é uma regra!
Com todas estas notas esclarecidas, adequamos os métodos de nosso arquivo TXT do seguinte modo:
class JButton <<Class>> {
+ JButton()
+ JButton(text: String)
+ JButton(text: String, icon: Icon)
+ JButton(a: Action)
+ JButton(icon: Icon)
+ getAccessibleContext(): AccessibleContext
+ getUIClassID(): String
+ isDefaultButton(): boolean
+ isDefaultCapable(): boolean
# paramString(): String
+ removeNotify()
+ setDefaultCapable(defaultCapable: boolean)
+ updateUI()
}
E atualizamos nosso PNG, reexecutando os comandos vistos anteriormente em nosso interpretador de comandos. O resultado obtido é:
Figura 2 - Representação da classe JButton com seus métodos.
Fonte: Autor (CC BY-NC-SA 4.0)
Observando a imagem gerada, percebemos que um detalhe nos escapou! Vejam que os símbolos dos modificadores não estão como esperávamos. Isso ocorre, pois o plantuml tem uma skinparam padrão para representar os modificadores. Para usar o formato que queremos, devemos incluir antes da declaração da classe a instrução:
skinparam classAttributeIconSize 0
Lembremos também de sempre salvar o arquivo, após qualquer alteração. Assim teremos na reexecução dos comandos a saída atualizada. Sem mais delongas, testamos e obtemos:
Figura 3 - Representação da classe JButton com seus métodos e modificadores (UML).
Fonte: Autor (CC BY-NC-SA 4.0)
Em nossa identificação, também percebemos que a classe JButton está contida em um package (pacote), a saber, javax.swing.JButton. Para representar o pacote, utilizamos a sintaxe:
package packageName {
class
}
Assim modificamos nosso arquivo-fonte:
@startuml classDiagram
skinparam classAttributeIconSize 0
package javax.swing {
class JButton <<Class>> {
+ JButton()
+ JButton(text: String)
+ JButton(text: String, icon: Icon)
+ JButton(a: Action)
+ JButton(icon: Icon)
+ getAccessibleContext(): AccessibleContext
+ getUIClassID(): String
+ isDefaultButton(): boolean
+ isDefaultCapable(): boolean
# paramString(): String
+ removeNotify()
+ setDefaultCapable(defaultCapable: boolean)
+ updateUI()
}
}
@enduml
E agora testamos. O resultado obtido é:
Figura 4 - Representação da classe JButton com o seu package javax.swing.
Fonte: Autor (CC BY-NC-SA 4.0)
O plantuml também nos permite usar packages estilizados, definidos por stereotypes, cuja sintaxe é:
package packageName <<stereotype>> {
class
}
Os stereotypes disponíveis são: <<Node>>, <<Rectangle>>, <<Folder>>, <<Frame>>, <<Cloud>> e <<Database>>.
Figura 5 - Exemplos de stereotypes aplicados ao package da classe JButton.
Fonte: Autor (CC BY-NC-SA 4.0)
Neste exemplo, escolhemos o stereotype <<Frame>>, mas fiquem à vontade para escolherem os seus, ok?
Pois bem, agora iniciaremos a representação dos relacionamentos da classe JButton com outros elementos. Mais uma vez, recorrendo ao JavaDoc, descobrimos que a classe JButton, estende à classe AbstractButton. Esse comportamento, de estender, é em OOP/POO (Object Oriented Programming/Programação Orientada a Objetos) chamado de Herança, e é representado na linguagem Java, pela palavra-reservada extends. Também vemos que a classe estendida por JButton, está no mesmo pacote, e que esta classe, por sua vez, não é uma simples classe, mas uma classe abstrata. Considerando tudo isso, estabeleçemos uma ordem de ações que é:
- Crie uma classe abstrata em um package já existente.
- Crie o relacionamento de extension, chamado em UML de especialização/generalização entre JButton e AbstractButton. Neste caso, AbstractButton é a classe generalizada (pai), enquanto JButton é a especializada (filha).
Para isto, incluímos dentro do package javax.swing e após a declaração da classe:
abstract AbstractButton <<Abstract Class>>
AbstractButton <|-- JButton
Atentemos ao símbolo <|-- que é padrão para representar extension no plantuml, bem como a ordem/direção entre as classes. Por fim, salvamos nosso arquivo, reexecutamos e obtemos:
Figura 6 - Relacionamento de Generalização/Especialização entre a classe abstrata AbstractButton e a classe JButton.
Nesta proposta também temos um relacionamento da classe JButton com a interface Accessible. Este é indicado na linguagem Java pela palavra-reservada implements, cuja representação no plantuml é o símbolo ..|>.
Para este relacionamento, primeiro criamos um novo package, para o qual pertence a interface. E dentro deste package criamos a interface, e por fim, criamos o relacionamento da interface com a classe JButton. Fizemos do seguinte modo:
package javax.accessibility <<Frame>> {
interface Accessible <<Interface>>
JButton ..|> Accessible
}
E testamos:
Figura 7 - Relacionamento entre a classe JButton e a interface javax.accessibility.Acessible.
Fonte: Autor (CC BY-NC-SA 4.0)
Bom, agora (opcionalmente) criamos um rótulo/label para os relacionamentos, substituindo as instruções:
- AbstractButton <|-- JButton por AbstractButton <|-- JButton : extends
- JButton ..|> Accessible por JButton ..|> Accessible : implements
Com isto feito, testamos e obtemos:
Figura 8 - Inclusão dos rótulos/labels extends e implements nos respectivos relacionamentos.
Fonte: Autor (CC BY-NC-SA 4.0)
Com isto temos, além da representação com os símbolos da UML, isto é, setas vazadas, com linhas sólidas ou tracejadas, um rótulo identificando cada relação.
Uma última observação olhando nosso diagrama até então, é que a interface e a classe abstrata tem campos vazios, que evitamos preencher já que queremos evidenciar apenas a classe JButton. Assim sendo, podemos ocultar os membros (atributos e métodos). Para tal, incluímos logo após a instrução skinparam classAttributeIconSize 0 o trecho:
hide members
Salvamos, testamos e obtemos:
Figura 9 - Diagrama com (todos) os membros ocultados.
Fonte: Autor (CC BY-NC-SA 4.0)
Hum... De fato, foram ocultados os membros, mantendo apenas o que nos interessa. No entanto, os membros da classe JButton, também foram ocultos, o que, não nos parece correto. Para solucionarmos isto, substituímos a instrução hide members, por hide empty members. A cláusula empty, indica que serão ocultos apenas membros de elementos vazios. Fizemos o ajuste e testamos! Assim obtemos:
Figura 10 - Diagrama com os membros ocultados de elementos vazios.
Fonte: Autor (CC BY-NC-SA 4.0)
Customize o diagrama
Além de nos permitir desenvolver o diagrama, podemos também deixá-lo com uma aparência mais convidativa, usando temas (themes) fornecidos pelo próprio framework.
Para tal, basta incluirmos no topo de nosso arquivo, logo após a instrução hide empty members o trecho !theme themeName. Os themeName disponíveis podem ser obtidos na seguinte lista.
Sem delongas, modifiquem seus arquivos e testem!
Figura 11 - Exemplos dos themes: amiga, crt-amber e plain.
Fonte: Autor (CC BY-NC-SA 4.0)
Para saber um pouco mais sobre Themes, vale a visita nos links Official Theme PlantUML Gallery e Puml Theme Gallery.
Defina novos formatos de saída
Por fim, testamos a geração de arquivos além do formato padrão do plantuml! Isto mesmo, é possível, gerar nosso arquivo final, em SVG, EPS, PDF, além de outros formatos de não-imagem. Para saber, todos os formatos disponíveis, além de outras opções disponíveis, executem:
java -jar plantuml.jar -help
Já para testar o diagrama com outros formatos de saída, usem a sintaxe:
java -jar plantuml.jar classDiagram.txt outputFormat
Como outputFormat usem os valores: -tpng | -tsvg | -teps | -tpdf | -tvdx | -thtml | entre outros, disponíveis.
Com isto, concluímos este post! Caso queiram se aprofundar no plantuml, e conhecer integrações, plugins, além de outras funcionalidades propostas por este framework, não deixem de consultar a PlantUML Language Reference Guide (PDF) e a documentação proposta por Hitchhiker’s.
Se gostaram, por favor, deixem seus COMENTÁRIOS, nos SIGAM e principalmente COMPARTILHEM para que assim, seja possível alcançar mais pessoas, que como vocês, curtem Programação, com conteúdos PRÁTICOS e GRATUITOS!
Para ver e/ou baixar o código-fonte deste post, ou mesmo melhorá-lo, visitem o repositório no GitHub.
Até a próxima!
Cite este material
FERNANDES, Fábio. Desenvolvendo um diagrama de classes (UML diagram). aprendaCodar, 02 de julho de 2022. Disponível em: <https://aprendacodar.blogspot.com/2022/07/conheca-o-plantuml-e-desenvolva.html>. Acesso em: