Dominando Diagramas de Classes UML: Um Estudo de Caso Compreensivo de um Sistema Telefônico

“Uma imagem vale mil linhas de código.”
— Essa máxima é verdadeira na engenharia de software, especialmente quando se utiliza Linguagem Unificada de Modelagem (UML) para visualizar sistemas complexos. Neste artigo, exploraremos um estudo de caso do mundo real de um sistema telefônico, usando um diagrama de classes UML meticulosamente elaborado como base. Analisaremos sua estrutura, examinaremos as relações e traduziremos o design em princípios práticos de desenvolvimento — tudo isso mantendo-se alinhado às melhores práticas da indústria.


🔷 Introdução: Por que os Diagramas de Classes UML Importam

No design de software orientado a objetos, Diagramas de Classes UML servem como o projeto arquitetônico de um sistema. Eles definem a estrutura estática — as classes, seus atributos, operações e como se relacionam entre si. Esses diagramas não são apenas para documentação; são ferramentas essenciais de comunicação entre desenvolvedores, partes interessadas e arquitetos.

Este artigo utiliza um diagrama de classes UML bem estruturado de um sistema telefônico para demonstrar como:

  • Identificar os componentes estruturais principais

  • Modelar relações com precisão

  • Aplicar princípios de design orientado a objetos

  • Traduzir modelos visuais em código limpo e sustentável

Vamos começar.


🧱 1. Componentes Estruturais Principais: Os Blocos de Construção do UML

Todo diagrama de classes começa com os elementos fundamentais: classesatributos, e operações.

✅ Classe: O Projeto dos Objetos

  • Representado por um retângulo azul dividido em três seções:

    • Topo: Nome da classe (por exemplo, Telefone)

    • Meio: Atributos (campos de dados)

    • Base: Operações (métodos)

Exemplo:

+-------------------+
|   Telefone        |
+-------------------+
| - gancho : boolean|
| - conexão : Linha |
+-------------------+
| + discar(n: int)  |
| + desligar()      |
| + ligar()         |
+-------------------+

✅ Atributos: Dados que Definem o Estado

  • Declarados na seção central da caixa da classe.

  • Antecedido por um símbolo de visibilidade:

    • - = privado (acessível apenas dentro da classe)

    • + = público (acessível de fora)

    • # = protegido (acessível em subclasses)

Exemplo:
- ocupado : boolean
Isso significa que o Linha classe rastreia se está atualmente em uso — mas apenas ela pode modificar esse estado diretamente.

✅ Operações (Métodos): Comportamento e Interação

  • Definido na seção inferior.

  • Siga o sintaxe: + nomeOperacao(parametros) : tipoRetorno

Exemplo:
+ discar(n: int) : void
Indica que um Telefone pode iniciar uma chamada para um número n.

💡 Melhor Prática: Use camelCase para nomes de métodos (foraDoGancho()discar()), e PascalCase para nomes de classes (TelefoneMáquina de Atendimento).


🔗 2. Relações e Associações: Como os Objetos Interagem

O verdadeiro poder de um diagrama de classes não reside nas classes individuais, mas nas relações entre elas. Essas conexões definem o comportamento dinâmico do sistema.

🔄 Associação: Uma Ligação Geral Entre Classes

Uma associação é uma relação em que uma classe conhece outra.

🔹 Nomes de Papel: Esclarecer o Contexto

  • No seu diagrama, conexão e telefonesConectados são nomes de papel.

  • Eles esclarecem o que a relação significa no contexto:

    • Telefone tem uma conexão para um Linha.

    • Linhamantém uma lista detelefonesConectados.

Isso evita ambiguidade: é “um telefone conectado a uma linha” ou “uma linha conectada a um telefone”? Os nomes de papel tornam claro.

🔹 Multiplicidade: O Lado Quantitativo das Relações

A multiplicidade definequantas instânciasde uma classe estão associadas a outra.

Multiplicidade Significado Exemplo
0..1 Zero ou um UmaTelefonepode estar conectado a zero ou umLinha
0..* Zero ou muitos UmaLinhapode suportar muitosTelefones
1 Exatamente um UmaMensagemdeve pertencer a exatamente umaMáquina de Resposta
* Muitos Um Linha pode ter muitos Telefones

⚠️ Nunca deixe a multiplicidade em branco — é uma restrição crítica que orienta a implementação e evita erros lógicos.


🧩 Agregação vs. Composição: A Relação “Todo-Parte”

Essas são formas especializadas de associação que descrevem propriedade e dependências de ciclo de vida.

Relação Indicador Visual Significado Exemplo
Agregação Diamante vazio (◇) Relação “tem-um”; a parte pode existir independentemente Telefone tem um Toque. Se o telefone for descartado, o toque ainda existe conceitualmente.
Composição Diamante preenchido (◆) “Tem-um” forte; a parte não pode existir sem o todo Máquina de Resposta possui Mensagem. Exclua a máquina → todas as mensagens são destruídas.

🔍 Ponto-chave:

  • Agregação: Propriedade compartilhada (por exemplo, um carro tem rodas, mas as rodas podem ser reutilizadas).

  • Composição: Propriedade exclusiva (por exemplo, uma casa tem quartos — se a casa for demolido, os quartos também são).

✅ Dica Profissional: No código, a agregação muitas vezes se traduz em um referência (ponteiro), enquanto a composição implica instanciação de objeto dentro do construtor do pai.


📡 3. Estudo de Caso: O Sistema de Telefonia — Uma Análise Aprofundada

Class Diagram, UML Diagrams Example: Telephone (Use of Association) -  Visual Paradigm Community Circle

Vamos percorrer a lógica do sistema conforme mostrado no diagrama.

🏗️ 1. A Estrutura: A Linha Classe

  • Gerencia o estado da conexão (ocupado : booleano)

  • Atua como um coordenador central para chamadas

  • Tem um multiplicidade de 0..* no telefonesConectados lado → uma única linha pode atender múltiplos telefones

🔄 Interação: Quando um Telefone discar, envia uma solicitação para o Linha para verificar a disponibilidade.

📱 2. A Interface do Usuário: A Telefone Classe

  • Centro principal do sistema

  • Contém:

    • gancho : booleano → rastreia se o fone está fora do gancho

    • conexão : Linha → referência para a linha ativa

  • Fornece operações principais:

    • discar(n: int) → inicia uma chamada

    • levantar() → levanta o fone

    • colocar() → coloca-o de volta

🎯 Princípio de Design: O Telefone classe permanece focada na interação do usuário — recursos complexos são delegados a outros componentes.

🛠️ 3. Componentes Modulares: Desacoplamento para Manutenibilidade

Para evitar que o Telefone classe se torne um “objeto de deus”, a funcionalidade é terceirizada para classes especializadas:

Componente Tipo Responsabilidade
Toque Agregação Reproduz som quando chega uma chamada
Identificador de Chamada Agregação Exibe o número do chamador em entrada
Máquina de Respostas Composição Grava e armazena mensagens

✅ Por que isso importa:

  • Se você precisar substituir o toque por um novo motor de som, você só modificará Toque — e não toda a Telefone.

  • A composição garante queintegridade dos dados: as mensagens estão vinculadas à máquina e não podem existir de forma independente.


✨ 4. Melhores práticas para desenhar diagramas de classes UML eficazes

Criar um diagrama UML de alta qualidade não é apenas sobre desenhar linhas — é sobreclareza, consistência e correção.

✅ 1. Use convenções de nomeação consistentes

  • Classes: Singular, PascalCase
    → TelefoneMensagemLinha

  • Atributos e Métodos: camelCase
    → offHook()getCallerId()isBusy()

❌ Evite:Telefonesnúmero_de_ligaçãoDialCall()

✅ 2. Mantenha Limpo — A Regra do ‘Nenhum Espaguete’

  • Evite cruzar linhas — reorganize classes para minimizar sobreposições.

  • Agrupe classes relacionadas juntas:

    • Coloque ToqueIdentificador_de_chamada, e Máquina_de_atendimento perto de Telefone

    • Mantenha Linha e Mensagem em um agrupamento lógico

🎨 Dica: Use ferramentas de layout (como StarUML, Visual Paradigm ou Lucidchart) para alinhar e organizar automaticamente.

✅ 3. Seja Preciso com a Multiplicidade

  • Nunca use * quando você quer dizer 1..*

  • Use 0..1 em vez de 1 se a relação for opcional

  • Pergunte sempre: “Este objeto pode existir sem o outro?”

🧠 Exemplo:
Um Mensagem deve pertencer a um Máquina de Respostas → use 1 no lado do Máquina de Respostas lado e * no lado do Mensagem lado.

✅ 4. Respeite a Encapsulamento

  • Atributos privados (-) → ocultar estado interno

  • Métodos públicos (+) → expor acesso controlado

🔒 Exemplo:
Linha não deve expor ocupado diretamente. Em vez disso:

+ isBusy() : boolean
+ setBusy(b: boolean) : void

Isso permite validação (por exemplo, impedir definir ocupado = verdadeiro a menos que a linha esteja livre).


🧩 5. Do Diagrama para o Código: Um Esqueleto Prático (Java e Python)

Vamos trazer o diagrama à vida com código. Abaixo estão esqueletos em Java e Python, mostrando como o UML se traduz em implementação no mundo real.

Implementação em Java (Continuação)

public class Telefone {
private boolean gancho; // verdadeiro = fora do gancho
private Linha conexao;
private Toque ringer;
private IdentificadorDeLigacao callerId;
private Atendedor answeringMachine;

public Telefone() {
    this.gancho = true; // inicialmente no gancho
    this.ringer = new Toque();
    this.callerId = new IdentificadorDeLigacao();
    this.answeringMachine = new Atendedor(); // Composição: criado aqui
}

}


---

### 🐍 **Implementação em Python (Limpa, Orientada a Objetos)**

```python
from typing import List, Optional

class Line:
    def __init__(self):
        self._busy: bool = False
        self._connected_phones: List['Telephone'] = []

    @property
    def busy(self) -> bool:
        return self._busy

    @busy.setter
    def busy(self, value: bool):
        self._busy = value

    def add_phone(self, phone: 'Telephone'):
        self._connected_phones.append(phone)

    def __str__(self):
        return f"Linha(ocupada={self._busy}, telefones={len(self._connected_phones)})"


class Ringer:
    def ring(self):
        print("🔔 Toque...")

    def stop(self):
        print("🔔 Toque parado.")


class CallerId:
    def display(self, number: int):
        print(f"📞 Chamada entrante de: {number}")


class Message:
    def __init__(self, caller_id: int, timestamp: str):
        self.caller_id = caller_id
        self.timestamp = timestamp

    def __str__(self):
        return f"Mensagem de {self.caller_id} às {self.timestamp}"


class AnsweringMachine:
    def __init__(self):
        self._messages: List[Message] = []
        self._activated: bool = False

    @property
    def activated(self) -> bool:
        return self._activated

    @activated.setter
    def activated(self, value: bool):
        self._activated = value

    def record_call(self, caller_id: int):
        from datetime import datetime
        timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        message = Message(caller_id, timestamp)
        self._messages.append(message)
        print(f"✅ Mensagem gravada: {message}")

    def get_messages(self) -> List[Message]:
        return self._messages.copy()

    def __str__(self):
        return f"Máquina de atendimento(mensagens={len(self._messages)}, ativada={self._activated})"


class Telephone:
    def __init__(self):
        self._hook: bool = True  # True = no gancho
        self._connection: Optional[Line] = None
        self._ringer = Ringer()
        self._caller_id = CallerId()
        self._answering_machine = AnsweringMachine()  # Composição: criado aqui

    def off_hook(self):
        self._hook = False
        print("📞 Telefone levantado do gancho.")
        if self._connection and not self._connection.busy:
            self._connection.busy = True
            self._ringer.ring()
        else:
            print("❌ Linha ocupada ou não conectada.")

    def on_hook(self):
        self._hook = True
        if self._connection:
            self._connection.busy = False
        self._ringer.stop()
        print("📞 Telefone colocado de volta no gancho.")

    def dial(self, number: int):
        if not self._connection:
            print("❌ Nenhuma linha conectada.")
            return
        if self._connection.busy:
            print("❌ Linha ocupada. Não é possível discar.")
            return

        print(f"📞 Discando: {number}")
        self._caller_id.display(number)

        if self._answering_machine.activated:
            self._answering_machine.record_call(number)
        else:
            self._ringer.ring()

    @property
    def hook(self) -> bool:
        return self._hook

    @property
    def connection(self) -> Optional[Line]:
        return self._connection

    @connection.setter
    def connection(self, line: Line):
        self._connection = line
        line.add_phone(self)

    def activate_answering_machine(self):
        self._answering_machine.activated = True
        print("🎙️ Máquina de atendimento ativada.")

    def __str__(self):
        status = "fora do gancho" if not self._hook else "no gancho"
        return f"Telefone(gancho={status}, conectado_a={self._connection})"


# === Exemplo de uso ===
if __name__ == "__main__":
    line = Line()
    phone = Telephone()
    phone.connection = line  # Estabelecer associação

    phone.off_hook()
    phone.dial(5551234)

    phone.activate_answering_machine()
    phone.dial(5555555)  # Será gravado

    print("n--- Estado do Sistema ---")
    print(phone)
    print(line)
    print(phone._answering_machine)

📌 Principais aprendizados: Do diagrama à entrega

Conceito UML Tradução de código Benefício do design
Agregação (◇) Campo de referência (por exemplo, Ringer ringer) Reutilização flexível, ciclo de vida independente
Composição (◆) Objeto criado dentro do construtor Propriedade forte, limpeza automática
Atributos privados privado campos com getter/setter Encapsulamento, integridade dos dados
Multiplicidade Lógica de validação nos métodos Evita estados inválidos
Nomes de papéis Nomes de métodos claros e semântica de variáveis Código auto-documentado

✅ Dicas finais para desenvolvedores e arquitetos

  1. Comece com o diagrama, não com o código.
    Um diagrama UML bem elaborado reduz retrabalho e lacunas de comunicação.

  2. Revise a multiplicidade com os interessados.
    Pergunte: “Um mensagem pode existir sem uma máquina?” → Não → Composição.

  3. Use as ferramentas com sabedoria.
    Ferramentas como Visual Paradigm, ou PlantUML ajudam a manter a consistência e gerar código automaticamente.

  4. Refatore cedo.
    Se uma classe tiver mais de 10 métodos ou 15 atributos, considere dividir a classe (Princípio da Responsabilidade Única).

  5. Trate o UML como um documento vivo.
    Atualize-o conforme os requisitos evoluírem — ele deve refletir a realidade, e não apenas uma visão passada.


🛠️ 6. Ferramentas com Visual Paradigm: Trazendo Diagramas UML à Vida

Embora entender os conceitos UML seja essencial, ferramentas eficazes é o que transforma ideias de design abstratas em modelos precisos, compartilháveis e sustentáveis. Entre as principais ferramentas para modelagem UML, Visual Paradigm se destaca como uma solução poderosa, intuitiva e pronta para empresas para criar, gerenciar e colaborar em diagramas de classes — especialmente para sistemas complexos como o sistema telefônico que exploramos.


✅ Por que Visual Paradigm? Uma perspectiva de desenvolvedor

Visual Paradigm (VP) é uma ferramenta abrangente de ferramenta de modelagem e design que suporta todo o ciclo de vida do desenvolvimento de software, desde os requisitos iniciais até a geração de código. Para equipes que trabalham com diagramas de classes UML, o VP oferece uma combinação única de precisão, automação e colaboração — tornando-o ideal tanto para iniciantes quanto para arquitetos experientes.

🔍 Principais Benefícios do Visual Paradigm:

Recursos Benefício
Interface de Arrastar e Soltar Crie instantaneamente classes, atributos, operações e relacionamentos sem escrever sintaxe.
Layout e Alinhamento Automáticos Mantém os diagramas limpos e profissionais — sem mais linhas em espiral ou caixas desalinhadas.
Validação em Tempo Real Sinaliza multiplicidades inválidas, visibilidade ausente ou associações inconsistentes enquanto você constrói.
Engenharia Bidirecional Gere código (Java, Python, C#, etc.) a partir de diagramas — ou reverse-engineie código existente para UML.
Colaboração em Equipe Compartilhe modelos por meio de espaço de trabalho na nuvem, comente elementos e acompanhe mudanças entre equipes.
Integração com IDEs e DevOps Exporte para PlantUML, Mermaid ou integre com Git, Jira e pipelines de CI/CD.

🎯 Passo a Passo: Criando o Sistema de Telefone no Visual Paradigm

Vamos passar por como você criaria o diagrama de classes do sistema de telefone usando o Visual Paradigm — do zero até um modelo de qualidade profissional.

Passo 1: Crie um Novo Projeto UML

  • Abra o Visual Paradigm.

  • Selecione “Novo Projeto” → Escolha “UML” → Selecione “Diagrama de Classes”.

  • Nomeie seu diagrama: Modelo_SistemaTelefônico.

Etapa 2: Adicionar Classes Principais

  • Do Paleta, arraste Classe ícones para a tela.

  • Renomeie-os: TelefoneLinhaToqueIdentificadorDeChamadaMáquinaDeAtendimentoMensagem.

  • Use PascalCase para nomes de classes (conforme prática recomendada).

Etapa 3: Definir Atributos e Operações

  • Clique duas vezes em uma classe para abrir seu Painel de Propriedades.

  • No Atributos aba, adicionar:

    - gancho : boolean
    - conexão : Linha
    - ocupado : boolean
    
  • Na Operações aba, adicionar:

    + desligar()
    + ligar()
    + discar(n: int) : void
    + estaOcupado() : boolean
    

💡 Dica: Use o “Adicionar” botão para inserir rapidamente atributos/operações. O VP sugere automaticamente a sintaxe com base nas configurações de linguagem.

Etapa 4: Modelar Relacionamentos com Precisão

Agora, conecte as classes usando o Ferramenta de Associação (a linha com uma seta):

  1. Linha ↔ Telefone (Associação com Papéis)

    • Desenhe uma linha entre Linha e Telefone.

    • Na Painel de Propriedades, defina:

      • Papel A (lado Linha)telefonesConectados → Multiplicidade: 0..*

      • Papel B (lado Telefone)conexão → Multiplicidade: 0..1

  2. Mensagem de Resposta → Mensagem (Composição)

    • Use o Composição ferramenta (losango preenchido).

    • Arraste de Mensagem de Resposta a Mensagem.

    • Defina a multiplicidade: 1 no Mensagem de Resposta lado, * no Mensagem lado.

  3. Telefone → Toque e Identificador de Chamada (Agregação)

    • Use Agregação (losango vazio).

    • Conecte Telefone a Toque e Identificador de Chamada.

    • Definir multiplicidade: 1 (Telefone) → 1 (Ringer) — significando um ringer por telefone.

✅ O Visual Paradigm renderiza automaticamente os símbolos corretos: ◇ para agregação, ◆ para composição.

Passo 5: Validar e Refinar

  • Use “Verificar Modelo” (under Ferramentas > Validar) para detectar:

    • Multiplicidades ausentes

    • Visibilidade inconsistente

    • Dependências circulares

  • Use “Layout Automático” para organizar o diagrama de forma organizada.

Passo 6: Gerar Código (ou Engenharia Reversa)

  • Clique com o botão direito no diagrama → “Gerar Código”.

  • Escolha a linguagem: Java ou Python.

  • Selecione a pasta de saída → Clique Gerar.

📌 Resultado: O VP gera classes limpas e bem estruturadas com encapsulamento adequado, assinaturas de métodos e relacionamentos — exatamente como os esqueletos de código que criamos anteriormente.

Etapa 7: Exportar e Compartilhar

  • Exporte o diagrama como:

    • PNG/SVG para relatórios ou apresentações

    • PDF para documentação

    • PlantUML/Mermaid código para integração em Markdown ou Confluence

  • Compartilhe por meio de Visual Paradigm Cloud — colabore em tempo real com membros da equipe.


🔄 Engenharia Bidirecional: A Mudança de Jogo

Uma das características mais poderosas do Visual Paradigm é engenharia bidirecional — a capacidade de ir do diagrama para o código e vice-versa.

Fluxo de Trabalho Exemplo:

  1. Comece com UML → Projete o sistema telefônico.

  2. Gere código Java/Python → Use-o em seu IDE.

  3. Modifique o código (por exemplo, adicione uma históricoDeChamadas lista em Máquina de Atendimento).

  4. Engenharia Reversa → O VP detecta alterações e atualiza o diagrama automaticamente.

✅ Nenhuma mais sincronização manual! O modelo permanece sincronizado com a implementação.


💼 Casos de Uso para Equipes e Organizações

Caso de Uso Como o VP Ajuda
Onboarding de Novos Desenvolvedores Diagramas visuais servem como documentação instantânea.
Revisões de Arquitetura de Sistemas Compartilhe diagramas com partes interessadas para feedback.
Modernização de Sistemas Legados Engenharia reversa de código antigo para UML para entendê-lo.
Documentação Ágil Mantenha os diagramas UML atualizados a cada sprint.
Ambientes Acadêmicos e de Treinamento Ensine conceitos UML visualmente com feedback em tempo real.

📦 Começando com o Visual Paradigm

  1. O que é um Diagrama de Classes? – Um Guia para Iniciantes em Modelagem UML: Este recurso fornece uma visão geral informativa explicando o propósito, componentes e importância dos diagramas de classes no desenvolvimento de software e no design de sistemas.

  2. Tutorial Completo de Diagrama de Classes UML para Iniciantes e Especialistas: Um guia passo a passo que conduz os usuários pelo processo de criação e entendimento de diagramas para dominar a modelagem de software.

  3. Gerador de Diagramas de Classes UML com Inteligência Artificial por Visual Paradigm: Esta ferramenta avançada utiliza inteligência artificial para gerar automaticamente diagramas de classes UML a partir de descrições em linguagem natural, simplificando o processo de design.

  4. Da descrição do problema ao diagrama de classes: análise textual com inteligência artificial: Este artigo explora como a IA podeconverter descrições de problemas em linguagem natural em diagramas de classes precisos para modelagem de software eficiente.

  5. Aprendendo diagramas de classes com o Visual Paradigm – ArchiMetric: Um artigo que destaca a plataforma como uma excelente escolha para desenvolvedores paramodelar a estrutura de um sistema no design orientado a objetos.

  6. Como desenhar diagramas de classes no Visual Paradigm – Guia do Usuário: Um guia técnico detalhado que explica oprocesso de software passo a passo de criação de diagramas de classes dentro do ambiente.

  7. Ferramenta online gratuita para diagramas de classes – crie diagramas de classes UML instantaneamente: Este recurso apresenta umaferramenta gratuita baseada na web para criar diagramas de classes UML profissionais rapidamente sem instalação local.

  8. Dominando diagramas de classes: uma exploração aprofundada com o Visual Paradigm: Um guia abrangente que oferece umaexploração técnica aprofundada da criação de diagramas de classes para modelagem UML.

  9. Diagrama de classes no UML: conceitos principais e melhores práticas: Um tutorial em vídeo que explica como representar oestrutura estática de um sistema, incluindo atributos, métodos e relacionamentos.

  10. Tutorial passo a passo de diagrama de classes usando o Visual Paradigm: Este tutorial apresenta os passos específicos necessários paraabrir o software, adicionar classes e criar um diagrama para arquitetura de sistemas.


🏁 Pensamentos Finais: Ferramentas como Habilitador do Design

Visual Paradigm não é apenas uma ferramenta de diagramação — é um companheiro de design que transforma conceitos teóricos de UML em planos açãoáveis e executáveis. Ao automatizar tarefas tediosas, impor boas práticas e permitir a colaboração, ele capacita equipes a:

  • Projetar mais rápido

  • Comunicar com mais clareza

  • Codificar com confiança

🌟 Seja você um desenvolvedor solitário esboçando um pequeno sistema ou um arquiteto de equipe construindo software corporativo, Visual Paradigm pontua a lacuna entre visão e realidade.


📌 Próximos Passos: Experimente Você Mesmo

Quer ver o diagrama do sistema telefônico em ação?
👉 Posso gerar um arquivo de projeto do Visual Paradigm pronto para importação (.vp) ou fornecer o código PlantUML para compartilhamento fácil.

Basta dizer a palavra — e vamos construir seu próximo sistema, uma classe de cada vez. 🛠️💡


🎯 Conclusão: Projete Primeiro, Codifique Depois

O estudo de caso do sistema telefônico demonstra como um simples Diagrama de Classes UML pode modelar um sistema do mundo real com precisão e clareza. Ao entender:

  • estrutura das classes,

  • relações entre eles,

  • E o princípios da POO como encapsulamento e composição,

Você pode projetar sistemas que sejam:

  • Manutenível

  • Escalável

  • Testável

  • Colaborativo

🌟 Lembre-se: Um ótimo diagrama não é apenas uma imagem — é um contrato entre designers, desenvolvedores e usuários.


🔗 Quer mais? Tente este desafio

✍️ Exercício: Amplie o sistema telefônico para suportar:

  • Encaminhamento de chamadas

  • Espera de chamada

  • Várias linhas por telefone

Use UML para modelar as novas classes e relacionamentos. Depois, implemente-os na sua linguagem preferida.

Me avise — ficarei feliz em gerar o diagrama e o código atualizados para você!