Especialização em Sistemas com Java 21 LTS • Módulo de Modelagem Orientada a Objetos


🗺️ Mapa Conceitual do Tópico

classDiagram
    class Pedido {
        -String codigo
        -LocalDateTime dataHora
        -StatusPedido status
        -Cliente cliente
        -List~ItemPedido~ itens
        +adicionarItem(Produto p, int qtd) void
        +removerItem(String sku) void
        +calcularTotal() BigDecimal
        +avancarStatus() void
    }

    class Cliente {
        -String cpf
        -String nome
        -String email
    }

    class ItemPedido {
        -Produto produto
        -int quantidade
        -BigDecimal precoUnitario
        +subTotal() BigDecimal
    }

    class Produto {
        -String sku
        -String nome
        -BigDecimal precoBase
    }

    class StatusPedido {
        <<enumeration>>
        AGUARDANDO_PAGAMENTO
        PAGO
        ENVIADO
        ENTREGUE
        CANCELADO
        +podeTransicionarPara(StatusPedido novo) boolean
    }

    Pedido *-- ItemPedido : "Composição Forte (1..*)"
    Pedido o-- Cliente : "Agregação (1..1)"
    ItemPedido o-- Produto : "Referência (1..1)"
    Pedido --> StatusPedido : "Estado do Domínio"

🏷️ 1. Enumerações Avançadas em Java (enum)

Em Java, um enum não é um mero conjunto de constantes numéricas (como em C++ ou C#), mas uma classe especializada de tipo seguro derivada implicitamente de java.lang.Enum<E>.

Características Notáveis:

  • Instâncias Únicas (Singletons): A JVM garante que cada constante do enum seja instanciada exatamente uma única vez durante o carregamento da classe.
  • Atributos e Construtores: Enums podem conter variáveis de instância final e construtores obrigatoriamente private.
  • Comportamentos Abstratos Polimórficos: Um enum pode declarar métodos abstratos que são obrigatoriamente implementados por cada constante, constituindo uma implementação elegante do padrão de projeto Strategy:
public enum TipoDesconto {
    NENHUM {
        @Override
        public BigDecimal aplicar(BigDecimal valor) { return valor; }
    },
    FIDELIDADE {
        @Override
        public BigDecimal aplicar(BigDecimal valor) { return valor.multiply(new BigDecimal("0.90")); }
    },
    BLACK_FRIDAY {
        @Override
        public BigDecimal aplicar(BigDecimal valor) { return valor.multiply(new BigDecimal("0.70")); }
    };

    public abstract BigDecimal aplicar(BigDecimal valor);
}

⚠️ Regra de Ouro: Nunca persista o .ordinal()

O método .ordinal() retorna o índice da constante baseado na ordem de declaração (0, 1, 2…). Se um desenvolvedor alterar a posição das constantes ou inserir uma nova no início, todos os registros pré-existentes no banco de dados serão corrompidos. Persista sempre o nome textual (.name()) ou um código de negócio fixo.


🧩 2. O Princípio Arquitetural: “Favor Composition Over Inheritance”

A composição é uma relação estrutural onde um objeto “tem-um” (has-a) outro objeto como parte de seu estado interno, ao invés de “ser-um” (is-a) através de herança hierárquica.

Por Que a Composição é Superior à Herança em Modelagem Corporativa:

  1. Desacoplamento: A herança quebra o encapsulamento porque a subclasse depende dos detalhes internos da superclasse (Fragile Base Class Problem). A composição interage unicamente através de contratos públicos.
  2. Flexibilidade em Tempo de Execução: Comportamentos compostos podem ser injetados ou substituídos dinamicamente sem alterar a estrutura de classes.
  3. Coesão Limpa: Cada classe foca exclusivamente em sua responsabilidade única (SRP), delegando tarefas especializadas para suas partes componentes.

📐 3. Composição Forte vs Agregação Fraca

No padrão UML e na modelagem em código Java:

  • Composição Forte (*--): As partes pertencem exclusivamente ao objeto “todo”. Se o Pedido for cancelado ou apagado da memória, seus ItemPedido perdem a razão de existir e são coletados pelo GC.
  • Agregação Fraca (o--): As partes possuem ciclo de vida independente. Se o Pedido deixar de existir, o Cliente continua existindo no banco de dados e na memória do sistema.

📊 4. Matriz Comparativa: Composição vs Agregação vs Herança

Dimensão Composição Forte Agregação Fraca Herança de Classes
Relação Semântica “Tem-um” (parte inseparável) “Tem-um” (relação contextual) “É-um” (especialização de tipo)
Ciclo de Vida Vinculado e dependente Totalmente independente Acoplado em tempo de compilação
Acoplamento Baixo Mínimo Muito Alto
Reaproveitamento Por delegação de chamadas Por delegação de chamadas Por extensão de código
Multiplicidade 1 para 1 ou 1 para N 1 para 1 ou 1 para N Estritamente simples em Java (extends)

💻 5. Código de Demonstração Corporativo (Java 21 LTS)

package com.empresa.composicao;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * Sistema de Pedidos corporativo demonstrando composição de entidades e ciclo de vida de enums.
 */
public class DominioVendasComposicao {

    public enum StatusPedido {
        CRIADO {
            @Override
            public boolean podeAvancarPara(StatusPedido proximo) { return proximo == PAGO || proximo == CANCELADO; }
        },
        PAGO {
            @Override
            public boolean podeAvancarPara(StatusPedido proximo) { return proximo == ENVIADO || proximo == CANCELADO; }
        },
        ENVIADO {
            @Override
            public boolean podeAvancarPara(StatusPedido proximo) { return proximo == ENTREGUE; }
        },
        ENTREGUE {
            @Override
            public boolean podeAvancarPara(StatusPedido proximo) { return false; }
        },
        CANCELADO {
            @Override
            public boolean podeAvancarPara(StatusPedido proximo) { return false; }
        };

        public abstract boolean podeAvancarPara(StatusPedido proximo);
    }

    public record Cliente(String id, String nome, String email) {}
    public record Produto(String sku, String titulo, BigDecimal preco) {}

    public static class ItemPedido {
        private final Produto produto;
        private final int quantidade;
        private final BigDecimal precoUnitario;

        public ItemPedido(Produto produto, int quantidade) {
            this.produto = produto;
            this.quantidade = quantidade;
            this.precoUnitario = produto.preco();
        }

        public BigDecimal calcularSubtotal() {
            return precoUnitario.multiply(new BigDecimal(quantidade));
        }

        public Produto getProduto() { return produto; }
        public int getQuantidade() { return quantidade; }
        public BigDecimal getPrecoUnitario() { return precoUnitario; }
    }

    public static class Pedido {
        private final String codigoPedido;
        private final LocalDateTime dataCriacao;
        private final Cliente cliente; // Agregação
        private final List<ItemPedido> itens = new ArrayList<>(); // Composição forte
        private StatusPedido status;

        public Pedido(String codigoPedido, Cliente cliente) {
            this.codigoPedido = codigoPedido;
            this.cliente = cliente;
            this.dataCriacao = LocalDateTime.now();
            this.status = StatusPedido.CRIADO;
        }

        public void adicionarItem(Produto produto, int quantidade) {
            if (this.status != StatusPedido.CRIADO) {
                throw new IllegalStateException("Não é possível alterar itens de um pedido com status: " + status);
            }
            itens.add(new ItemPedido(produto, quantidade));
        }

        public void transicionarPara(StatusPedido novoStatus) {
            if (!this.status.podeAvancarPara(novoStatus)) {
                throw new IllegalStateException(String.format("Transição ilegal: de %s para %s", status, novoStatus));
            }
            this.status = novoStatus;
        }

        public BigDecimal calcularTotal() {
            return itens.stream()
                    .map(ItemPedido::calcularSubtotal)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
        }

        public String getCodigoPedido() { return codigoPedido; }
        public Cliente getCliente() { return cliente; }
        public StatusPedido getStatus() { return status; }
        public List<ItemPedido> getItens() { return Collections.unmodifiableList(itens); } // Cópia defensiva
    }

    public static void main(String[] args) {
        Cliente cli = new Cliente("CLI-102", "Mariana Vasconcelos", "mariana@email.com");
        Pedido pedido = new Pedido("PED-2026-001", cli);

        pedido.adicionarItem(new Produto("SKU-NOTE", "MacBook Pro M3", new BigDecimal("14500.00")), 1);
        pedido.adicionarItem(new Produto("SKU-MOUSE", "Mouse Magic 3", new BigDecimal("750.00")), 2);

        System.out.println("================================================================================");
        System.out.printf("Pedido: %s | Cliente: %s | Total: R$ %.2f%n",
                pedido.getCodigoPedido(), pedido.getCliente().nome(), pedido.calcularTotal());
        System.out.printf("Status Atual: %s%n", pedido.getStatus());

        pedido.transicionarPara(StatusPedido.PAGO);
        System.out.printf("Status Atualizado: %s%n", pedido.getStatus());

        pedido.transicionarPara(StatusPedido.ENVIADO);
        System.out.printf("Status Atualizado: %s%n", pedido.getStatus());
        System.out.println("================================================================================");
    }
}