Guia de estudo

Aula 19 · Ter, 23/06

Introdução à UML

Desenhar as classes antes de codar.

Já sabemos escrever classes, herança e composição. Hoje aprendemos a DESENHAR isso: o diagrama de classes da UML, a notação de visibilidade e relações, e como traduzir cada seta direto pra código Python.

diagrama de classes visibilidade +/-/# herança e composição UML → Python

roteiro da aula

O que vamos aprender hoje

01

O que é UML

A linguagem visual padrão pra modelar sistemas orientados a objetos — independente de linguagem.

02

A caixa de classe

Os três compartimentos (nome, atributos, métodos) e a visibilidade +/-/#.

03

As relações

As setas: herança, composição, agregação e associação — cada uma é uma frase.

04

UML → código

Traduzir o diagrama pra classes Python: o ciclo modelar → codar.

ponte com as aulas anteriores

De onde paramos — sabemos codar POO

  • 🧱 Já escrevemos classes com atributos e métodos (aulas 02, 03), encapsulamento (04), herança (07) e composição (05).
  • 🔗 E na aula 18 guardamos VÁRIOS objetos em coleções (self.alunos = []). Na UML isso vira a multiplicidade *: "1 Turma tem * Alunos" — as listas da 18 são as associações 1→* do diagrama.
  • 🗺️ Hoje a gente não aprende um recurso NOVO de Python — aprende a DESENHAR o que já sabe escrever.
  • 🧭 UML é o mapa antes da viagem: você vê a estrutura inteira do sistema antes de digitar a primeira linha.

o problema

O problema: codar sem planejar

  • 🔨 Sair codando direto, sem pensar na estrutura, costuma dar em retrabalho: classes que não encaixam, relações erradas, refação.
  • 📐 Um arquiteto não levanta a parede antes da planta. UML é a planta do seu sistema de classes.
  • 👥 E é um idioma comum: o diagrama comunica a estrutura pra qualquer pessoa do time, mesmo antes de existir código.

definição

O que é UML

  • 📖 UML = Unified Modeling Language (Linguagem de Modelagem Unificada). Uma linguagem VISUAL padrão pra descrever sistemas orientados a objetos.
  • 🌐 É independente de linguagem de programação: o mesmo diagrama serve pra Python, Java ou C#. Ela modela a IDEIA, não o código.
  • 🎯 A UML tem vários tipos de diagrama. Hoje focamos só no mais usado no dia a dia: o diagrama de classes.

panorama

Os diagramas da UML (e qual usamos)

item detalhe
Diagrama de classes ⭐ Mostra as classes, seus atributos/métodos e as relações entre elas. É o nosso foco de hoje.
Diagrama de sequência Mostra a ordem das mensagens trocadas entre objetos ao longo do tempo. (Só citado.)
Diagrama de casos de uso Mostra o que cada tipo de usuário pode fazer no sistema. (Só citado.)
Diagrama de estados Mostra os estados pelos quais um objeto passa (ex.: pedido: criado → pago → enviado). (Só citado.)

a caixa de classe

A caixa de classe — 3 compartimentos

Toda classe na UML é uma caixa dividida em três faixas: o nome no topo, os atributos no meio, os métodos embaixo.

Diagrama UML da classe Aluno com os três compartimentos: nome no topo, atributos no meio (+ nome, + matricula, - cpf) e métodos embaixo (matricular, media).
Topo: o nome da classe. Meio: os atributos (os dados). Base: os métodos (as ações). O __init__ fica subentendido — não precisa aparecer na caixa.

a caixa de classe

A mesma classe em Python

A caixa do slide anterior vira código quase 1 pra 1: o nome vira class, os atributos viram self.algo, os métodos viram def.

aluno.py
class Aluno:
    def __init__(self, nome, matricula, cpf):
        self.nome = nome              # + público
        self.matricula = matricula    # + público
        self.__cpf = cpf              # - privado (dois underscores)

    def matricular(self, turma):
        print(f"{self.nome} entrou na turma {turma}")

    def media(self):
        return 0.0

aluno = Aluno("Ana", "2024001", "111.222.333-44")
print(aluno.nome)               # Ana
print(aluno.matricula)          # 2024001
aluno.matricular("INFO-1")      # Ana entrou na turma INFO-1
print(aluno.media())            # 0.0
Os corpos são simples de propósito: media() devolve 0.0 por enquanto e matricular() só registra a entrada — o foco aqui é ver a caixa do s7 virando estrutura de código. Atributos = self.algo no __init__; métodos = def dentro da classe.

a caixa de classe

Visibilidade: + - #

item detalhe
+ público Qualquer um acessa de fora. No Python: atributo/método normal, sem underscore (self.nome).
- privado Só a própria classe acessa. No Python: dois underscores no início (self.__cpf). Aula 04.
# protegido A classe e suas filhas acessam. No Python: um underscore (self._saldo) — convenção. Aula 04.

a caixa de classe

Visibilidade na prática

Os mesmos três níveis, lado a lado: o símbolo na caixa UML e o que ele vira em Python (a convenção de underscores da aula 04).

Na caixa UML

conta.txt
+----------------------+
|     ContaBancaria    |
+----------------------+
|  + titular: str      |
|  # saldo: float      |
|  - senha: str        |
+----------------------+

Em Python

conta.py
class ContaBancaria:
    def __init__(self, titular, senha):
        self.titular = titular    # + público
        self._saldo = 0.0         # # protegido
        self.__senha = senha      # - privado
Regra: + (público) = sem underscore; # (protegido) = um underscore _; - (privado) = dois underscores __. Não inverta — + é o aberto, - é o fechado.

a caixa de classe

Tipos e parâmetros na caixa

A UML também anota o TIPO de cada atributo (depois do :) e os parâmetros e o retorno de cada método.

Diagrama UML da classe ContaBancaria mostrando o tipo de cada atributo após os dois-pontos e os parâmetros e retornos dos métodos.
Leia atributo: nome: tipo (titular é uma str). Leia método: nome(parâmetro: tipo): tipo_de_retorno. ver_saldo(): float devolve um float; depositar(...): None não devolve nada.

as relações

As relações entre classes

  • Herança (generalização): triângulo vazio apontando pro pai. "Pato É UM Animal" — aula 07.
  • Composição: losango CHEIO. "Casa CONTÉM Cômodos, e os cômodos somem junto com a casa" — aula 05.
  • Agregação: losango VAZIO. "Time USA Jogadores, mas o jogador existe sozinho" — aula 05.
  • Associação simples: uma linha. "Médico CONHECE Paciente: um sabe do outro", sem a ideia de todo-parte.

as relações

Herança (generalização) → △

O triângulo vazio aponta pro PAI. A frase é sempre "É UM": Aluno é um Pessoa, Professor é um Pessoa.

Diagrama UML de herança: Pessoa como classe base, com Aluno e Professor ligados a ela por uma seta de triângulo vazio apontando para o pai.
O triângulo SEMPRE aponta pro pai (a classe mais geral). Aluno e Professor herdam tudo de Pessoa e ainda acrescentam o que é só deles.

as relações

Herança UML → Python

O triângulo △ da UML vira (Pai) no Python. O super().__init__() reaproveita o construtor do pai.

heranca.py
class Pessoa:
    def __init__(self, nome, cpf):
        self.nome = nome
        self.__cpf = cpf
    def __str__(self):
        return f"Pessoa: {self.nome}"

# herança: o triângulo △ da UML vira (Pessoa) no Python
class Aluno(Pessoa):
    def __init__(self, nome, cpf, matricula):
        super().__init__(nome, cpf)   # reaproveita o __init__ do pai
        self.matricula = matricula
    def __str__(self):
        return f"Aluno: {self.nome} (mat. {self.matricula})"

ana = Aluno("Ana", "111.222.333-44", "2024001")
print(ana)                  # Aluno: Ana (mat. 2024001)
print(isinstance(ana, Pessoa))   # True  (Aluno É UM Pessoa)
△ na UML = (Pai) no Python. A frase "Aluno é um Pessoa" se confirma no isinstance(ana, Pessoa) == True.

as relações

Composição → ◆ (losango cheio)

Losango CHEIO no lado do TODO. A frase é "CONTÉM": o todo cria a parte, e a parte morre junto com ele.

Diagrama UML de composição: Pedido ligado a ItemPedido por um losango cheio do lado do Pedido, com multiplicidade 1 para muitos.
Composição = todo-parte forte. No Python: o todo cria a parte dentro de si, no próprio __init__ (self.itens = [...]). Sem o Pedido, os itens não existem.

as relações

Agregação → ◇ (losango vazio)

Losango VAZIO no lado do todo. A frase é "USA": o todo recebe a parte pronta, e ela vive independente.

Diagrama UML de agregação: Turma ligada a Professor por um losango vazio do lado da Turma.
Agregação = todo-parte fraco. No Python: o objeto pronto chega de fora, pelo parâmetro do __init__ (def __init__(self, professor): self.professor = professor).

as relações · a confusão clássica

Composição vs Agregação

As duas são "TEM UM". O que muda é o ciclo de vida da parte: a composição cria a parte, a agregação só usa uma que já existe.

Composição ◆ — vida junta

composicao.py
class Pedido:
    def __init__(self, numero):
        self.numero = numero
        # CRIA a parte dentro de si
        self.itens = []
# sem o Pedido, os itens não existem.
# losango CHEIO.

Agregação ◇ — vida separada

agregacao.py
class Turma:
    def __init__(self, professor):
        # RECEBE a parte já pronta
        self.professor = professor
# o professor existe sem a turma.
# losango VAZIO.
Teste rápido: "se o todo for destruído, a parte some junto?". Some junto → composição ◆ (cheio). Continua existindo → agregação ◇ (vazio).

as relações

Associação e multiplicidade

Uma linha simples liga classes que se conhecem. Os números nas pontas (a multiplicidade) dizem QUANTOS de cada lado.

Diagrama UML de associação simples entre Turma e Aluno, com multiplicidade 1 de um lado e asterisco (vários) do outro.
A multiplicidade evita ambiguidade: "uma Turma tem VÁRIOS Alunos" (1 para *), mas "cada Aluno está em 1 Turma". Sem os números, o leitor teria que adivinhar.

as relações

Classe abstrata na UML

Uma classe abstrata (ABC, aula 10) aparece com o estereótipo «abstract» e, na tradição, o nome em itálico.

Diagrama UML de classe abstrata: ItemAcervo com o método abstrato descricao() em itálico, e a subclasse Livro que o implementa.
📍 Classe abstrata foi a aula 10: ela define o contrato (descricao()) mas não é instanciada direto. Na UML, o «abstract» avisa "esta classe é um molde, não um objeto pronto".

lendo o todo

Diagrama completo de um mini sistema

Juntando tudo: herança (△), associação com multiplicidade (1/*) e agregação (◇) num diagrama só.

Diagrama UML completo com quatro classes: Pessoa, Aluno, Turma e Professor, combinando herança, associação com multiplicidade e agregação.
Três relações num desenho: Aluno É UM Pessoa (△); 1 Turma tem * Alunos (associação); e a Turma USA um Professor (agregação ◇).

lendo o todo

Lendo o diagrama em voz alta

  • 🔼 "Aluno É UM Pessoa" — o triângulo aponta pra Pessoa, então Aluno herda nome e cpf dela.
  • 🔢 "Uma Turma TEM vários Alunos" — a multiplicidade 1 para * conta os dois lados da associação.
  • "A Turma USA um Professor" — losango vazio: o professor existe independente da turma (agregação).
  • 🗣️ A regra: toda relação vira uma frase. Se você consegue ler o diagrama em voz alta, você entendeu o sistema.

na prática

Ferramentas: draw.io e PlantUML

  • 🖱️ draw.io (diagrams.net): você ARRASTA as caixas e setas com o mouse. Visual, gratuito, ótimo pra começar.
  • ⌨️ PlantUML: você ESCREVE o diagrama como texto e ele gera a imagem. Bom pra versionar junto com o código (é texto puro).
  • ✏️ No papel ou no quadro também vale! O importante é pensar a estrutura antes — a ferramenta é só o meio.

na prática

PlantUML — o diagrama em texto

No PlantUML, o diagrama inteiro é texto entre @startuml e @enduml. Cada relação tem um símbolo de seta.

escola.puml
@startuml
skinparam classAttributeIconSize 0

class Pessoa {
  + nome: str
  - cpf: str
}
class Aluno {
  + matricula: str
}
class Professor {
  + salario: float
}
class Turma {
  + codigo: str
}

Aluno --|> Pessoa
Professor --|> Pessoa
Turma o-- Professor
Turma "1" -- "*" Aluno
@enduml
No PlantUML: --|> é herança (a ponta |> é o triângulo), o-- é agregação (o losango vazio), e -- é associação. A linha skinparam classAttributeIconSize 0 faz aparecer os símbolos + - # nos atributos (em vez das bolinhas coloridas), igual aos slides. O texto vira imagem automaticamente — sem arrastar caixa nenhuma.

ao vivo · vamos construir juntos

Exercício ao Vivo: do diagrama ao código

Partindo do diagrama do próximo slide, a gente implementa juntos. Sistema: um Carro que É UM Veiculo (herança) e que CONTÉM um Motor (composição). Acompanhem a tradução de cada seta pra código.

1

Leia o diagrama

Veiculo é a base. Carro ──△ Veiculo (herança, triângulo apontando pro pai). Carro ◆── Motor (composição, losango cheio: o carro cria o motor).

2

Classe Motor (a parte)

Com potencia e um __str__ que devolva algo como Motor 75cv. É a peça que o carro vai conter.

3

Herança: Carro(Veiculo)

Veiculo guarda a marca; Carro herda dele com class Carro(Veiculo) e chama super().__init__(marca) no construtor.

4

Composição: o motor dentro do carro

No __init__ do Carro, crie o motor com self.motor = Motor(potencia) — o carro CRIA o próprio motor. Feche com um __str__ que use o motor.

ao vivo · vamos construir juntos

O diagrama do nosso sistema

Em vez de desenhar no quadro, partimos deste diagrama. Leia as duas setas — e nos próximos slides a gente traduz cada uma pra código.

Diagrama UML do exercício: Veiculo (base, com marca) no topo; Carro herda de Veiculo por uma seta de triângulo vazio; e Carro contém Motor (com potencia) por um losango cheio de composição.
Duas relações pra traduzir: Carro É UM Veiculo (herança △ → class Carro(Veiculo) + super()) e Carro CONTÉM um Motor (composição ◆ → o carro cria self.motor dentro de si). A caixa do Carro não declara nada novo: ele herda a marca do Veiculo e ganha o motor pela composição.

resolução · passos 1 e 2

As peças: Motor e Veiculo

Começamos pelas classes que não dependem do carro: o Motor (a parte que ele vai conter) e o Veiculo (a base da herança).

🧩
Motor não sabe que existe um carro — é uma peça independente, com seu próprio __str__.
🏗️
Veiculo é a base: guarda o que todo veículo tem (a marca). O Carro vai herdar dela.
carro.py
# Motor é PARTE do Carro (composição ◆): o Carro cria o próprio motor
class Motor:
    def __init__(self, potencia):
        self.potencia = potencia
    def __str__(self):
        return f"Motor {self.potencia}cv"

class Veiculo:
    def __init__(self, marca):
        self.marca = marca
Duas classes que não dependem do Carro: a parte (Motor) e a base (Veiculo). Modelar primeiro deixou claro o que precisa existir antes.

resolução · passos 3 e 4

Carro(Veiculo): herança + composição

Agora o Carro: ele herda de Veiculo com super() e contém um Motor criado no próprio __init__.

🧬
super().__init__(marca) reaproveita o construtor do pai — o triângulo △ da UML virou isso.
🔩
self.motor = Motor(potencia) cria o motor DENTRO do carro — o losango ◆ da composição virou isso.
carro.py
# Carro É UM Veiculo (herança △)
class Carro(Veiculo):
    def __init__(self, marca, potencia):
        super().__init__(marca)           # herança: reaproveita o pai
        self.motor = Motor(potencia)      # composição: cria o motor dentro de si
    def __str__(self):
        return f"{self.marca}{self.motor}"

carro = Carro("Fiat", 75)
print(carro)
print(isinstance(carro, Veiculo))
Cada seta do diagrama virou uma linha de código: o triângulo △ → class Carro(Veiculo) + super(); o losango ◆ → self.motor = Motor(potencia). Modelar primeiro deixou o código óbvio.

resolução · o código completo

Tudo junto: a resolução completa

As peças que montamos passo a passo, agora num arquivo só — é exatamente isto que você roda. Repare como herança e composição convivem no mesmo Carro.

🧬
class Carro(Veiculo) + super() é a herança △; self.motor = Motor(...) é a composição ◆ — as duas no mesmo __init__.
▶️
Rodando este arquivo sai a marca herdada e o motor que ele contém — a saída exata vem no próximo slide.
carro.py
# Motor é PARTE do Carro (composição ◆): o Carro cria o próprio motor
class Motor:
    def __init__(self, potencia):
        self.potencia = potencia
    def __str__(self):
        return f"Motor {self.potencia}cv"

class Veiculo:
    def __init__(self, marca):
        self.marca = marca

# Carro É UM Veiculo (herança △)
class Carro(Veiculo):
    def __init__(self, marca, potencia):
        super().__init__(marca)           # herança: reaproveita o pai
        self.motor = Motor(potencia)      # composição: cria o motor dentro de si
    def __str__(self):
        return f"{self.marca}{self.motor}"

carro = Carro("Fiat", 75)
print(carro)
print(isinstance(carro, Veiculo))
Mesmo código dos slides anteriores, agora inteiro: as peças (Motor, Veiculo), a montagem (Carro) e o teste. Cada seta do diagrama virou uma linha — modelar primeiro deixou o código óbvio.

resolução · saída esperada

A saída no terminal

Rodando o carro.py, é isto que aparece no terminal:

🖨️
print(carro) chamou o __str__ do Carro, que por dentro usou o __str__ do Motor — a composição aparecendo na saída.
isinstance(carro, Veiculo) devolve True: confirma que o Carro É UM Veiculo (a herança).
terminal
Fiat — Motor 75cv
True
Herança e composição funcionando juntas: o Carro mostra a marca (que herdou) e o motor (que contém), e ainda É UM Veiculo.

recapitulando

Glossário rápido de hoje

item detalhe
UML Unified Modeling Language: linguagem visual padrão pra modelar sistemas orientados a objetos.
Diagrama de classes Mostra classes (caixas), atributos, métodos e as relações entre elas.
Visibilidade + - # Público (sem underscore) / privado (__) / protegido (_). Aula 04.
Generalização △ Herança "é um": triângulo vazio apontando pro pai. Vira class Filho(Pai). Aula 07.
Composição ◆ / Agregação ◇ Todo-parte: ◆ a parte some junto (criada dentro) / ◇ a parte vive sozinha (recebida pronta). Aula 05.
Associação e multiplicidade Linha simples ligando classes; 1, * e 1..* dizem quantos de cada lado participam.

O que dominamos hoje

  • Sei o que é UML e pra que serve o diagrama de classes: planejar a estrutura antes de codar.
  • Desenho a caixa de uma classe com os três compartimentos (nome, atributos, métodos).
  • Uso a visibilidade + (público), - (privado) e # (protegido), casando com os underscores do Python.
  • Reconheço e desenho herança (△), composição (◆), agregação (◇) e associação com multiplicidade.
  • Sei a diferença entre composição ◆ (a parte some junto) e agregação ◇ (a parte vive sozinha).
  • Traduzo um diagrama UML em classes Python: △ vira (Pai)+super(), ◆ vira a parte criada no __init__.

exercícios

Mãos à obra: os exercícios

Quatro exercícios em três níveis, do desenho ao código e de volta ao desenho. Comece em sala; termine o que faltar em casa. Mire pelo menos um de cada nível.

01

Fácil · Desenhe a caixa

Dada a classe ContaBancaria com titular (público), __saldo (privado) e os métodos depositar(valor) e sacar(valor), desenhe a caixa UML com os três compartimentos e a visibilidade correta: + titular: str, - saldo: float, + depositar(valor): None, + sacar(valor): None. Use uma ferramenta (draw.io ou PlantUML) e entregue em PDF. Foco: a caixa de classe + visibilidade.

02

Fácil · Identifique a relação

Para cada par abaixo, desenhe o trecho de diagrama UML numa ferramenta (draw.io ou PlantUML): as duas caixas e a seta correta entre elas — triângulo △ pra herança, losango ◆ pra composição, losango ◇ pra agregação, no lado certo. Embaixo de cada um, escreva a relação e justifique em 1 frase ("é um" / "tem um e some junto" / "usa um que existe sozinho"). Entregue os quatro num único PDF. Os pares: (a) Smartphone e Telefone; (b) Livro e Página; (c) Playlist e Música; (d) Floresta e Árvore. Foco: traduzir cada relação na seta certa. Dica de ouro: na dúvida entre ◆ e ◇, pergunte "se o todo acabar, a parte some junto?".

03

Médio · Da UML ao código

Dado o diagrama: Funcionario (base) com + nome: str e + salario: float; Gerente ──△ Funcionario (herança) com + equipe: list; e Gerente ◆── Projeto (composição). Implemente as três classes em Python com __init__ e __str__, respeitando a herança (super().__init__) e a composição (o Projeto é criado dentro do Gerente). Inclua um bloco de teste que cria um gerente e imprime, e entregue o arquivo .py. Foco: ler o diagrama e traduzir cada seta pra código.

04

Desafio · Modele antes de codar

Escolha UM mini sistema: (a) Locadora de jogos, (b) Cafeteria ou (c) Clínica. Primeiro DESENHE o diagrama de classes numa ferramenta (draw.io ou PlantUML), com pelo menos 3 classes, 1 herança e 1 composição ou agregação, incluindo visibilidade e multiplicidade. Depois IMPLEMENTE em Python a partir do SEU diagrama, com __str__ em cada classe e um bloco de teste. Entregue o diagrama em PDF e o código no arquivo .py. Foco: o ciclo completo modelar → codar (preparação pro projeto final da aula 25).

próxima aula

Aula 20 · 29/06

Padrões de projeto — Singleton e Factory Method

Agora que sabemos modelar com UML, vamos aprender SOLUÇÕES prontas pra problemas que se repetem no design de software — os padrões de projeto.

Singleton Factory padrões criacionais